ironnails 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +21 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/ironnails.gemspec +96 -0
- data/lib/iron_nails.rb +1 -0
- data/lib/ironnails/bin/IronNails.Library.dll +0 -0
- data/lib/ironnails/bin/IronRuby.Libraries.Yaml.dll +0 -0
- data/lib/ironnails/bin/IronRuby.Libraries.dll +0 -0
- data/lib/ironnails/bin/IronRuby.dll +0 -0
- data/lib/ironnails/bin/Microsoft.Dynamic.dll +0 -0
- data/lib/ironnails/bin/Microsoft.Scripting.Core.dll +0 -0
- data/lib/ironnails/bin/Microsoft.Scripting.ExtensionAttribute.dll +0 -0
- data/lib/ironnails/bin/Microsoft.Scripting.dll +0 -0
- data/lib/ironnails/config/configuration.rb +141 -0
- data/lib/ironnails/config/initializer.rb +144 -0
- data/lib/ironnails/controller/base.rb +135 -0
- data/lib/ironnails/controller/view_operations.rb +101 -0
- data/lib/ironnails/controller.rb +2 -0
- data/lib/ironnails/core_ext/array.rb +15 -0
- data/lib/ironnails/core_ext/class/attribute_accessors.rb +57 -0
- data/lib/ironnails/core_ext/class.rb +8 -0
- data/lib/ironnails/core_ext/fixnum.rb +22 -0
- data/lib/ironnails/core_ext/hash.rb +32 -0
- data/lib/ironnails/core_ext/kernel.rb +26 -0
- data/lib/ironnails/core_ext/string.rb +58 -0
- data/lib/ironnails/core_ext/symbol.rb +78 -0
- data/lib/ironnails/core_ext/system/net/web_request.rb +110 -0
- data/lib/ironnails/core_ext/system/security/secure_string.rb +18 -0
- data/lib/ironnails/core_ext/system/windows/markup/xaml_reader.rb +6 -0
- data/lib/ironnails/core_ext/system/windows/ui_element.rb +17 -0
- data/lib/ironnails/core_ext/time.rb +28 -0
- data/lib/ironnails/core_ext.rb +12 -0
- data/lib/ironnails/errors.rb +19 -0
- data/lib/ironnails/iron_xml.rb +83 -0
- data/lib/ironnails/logger.rb +4 -0
- data/lib/ironnails/logging/buffered_logger.rb +137 -0
- data/lib/ironnails/logging/class_logger.rb +29 -0
- data/lib/ironnails/models/base.rb +16 -0
- data/lib/ironnails/models/bindable_collection.rb +15 -0
- data/lib/ironnails/models/model_mixin.rb +69 -0
- data/lib/ironnails/models.rb +3 -0
- data/lib/ironnails/nails_engine.rb +398 -0
- data/lib/ironnails/observable.rb +117 -0
- data/lib/ironnails/security/secure_string.rb +61 -0
- data/lib/ironnails/version.rb +11 -0
- data/lib/ironnails/view/collections.rb +117 -0
- data/lib/ironnails/view/commands/add_sub_view_command.rb +33 -0
- data/lib/ironnails/view/commands/behavior_command.rb +29 -0
- data/lib/ironnails/view/commands/command.rb +208 -0
- data/lib/ironnails/view/commands/event_command.rb +32 -0
- data/lib/ironnails/view/commands/timed_command.rb +40 -0
- data/lib/ironnails/view/commands.rb +5 -0
- data/lib/ironnails/view/view.rb +190 -0
- data/lib/ironnails/view/view_model.rb +45 -0
- data/lib/ironnails/view/xaml_proxy.rb +226 -0
- data/lib/ironnails/view.rb +5 -0
- data/lib/ironnails/wpf.rb +113 -0
- data/lib/ironnails/wpf_application.rb +30 -0
- data/lib/ironnails.rb +68 -0
- metadata +133 -0
@@ -0,0 +1,137 @@
|
|
1
|
+
# Inspired by the Buffered Logger from Ezra (Merb)
|
2
|
+
# Taken from active_support
|
3
|
+
module IronNails
|
4
|
+
|
5
|
+
module Logging
|
6
|
+
|
7
|
+
|
8
|
+
class BufferedLogger
|
9
|
+
module Severity
|
10
|
+
DEBUG = 0
|
11
|
+
INFO = 1
|
12
|
+
WARN = 2
|
13
|
+
ERROR = 3
|
14
|
+
FATAL = 4
|
15
|
+
UNKNOWN = 5
|
16
|
+
end
|
17
|
+
include Severity
|
18
|
+
|
19
|
+
MAX_BUFFER_SIZE = 1000
|
20
|
+
|
21
|
+
# Set to false to disable the silencer
|
22
|
+
cattr_accessor :silencer
|
23
|
+
self.silencer = true
|
24
|
+
|
25
|
+
# Silences the logger for the duration of the block.
|
26
|
+
def silence(temporary_level = ERROR)
|
27
|
+
if silencer
|
28
|
+
begin
|
29
|
+
old_logger_level, self.level = level, temporary_level
|
30
|
+
yield self
|
31
|
+
ensure
|
32
|
+
self.level = old_logger_level
|
33
|
+
end
|
34
|
+
else
|
35
|
+
yield self
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_accessor :level
|
40
|
+
attr_reader :auto_flushing
|
41
|
+
attr_reader :buffer
|
42
|
+
|
43
|
+
def initialize(log, level = DEBUG)
|
44
|
+
@level = level
|
45
|
+
@buffer = []
|
46
|
+
@auto_flushing = 1
|
47
|
+
@no_block = false
|
48
|
+
|
49
|
+
if log.respond_to?(:write)
|
50
|
+
@log = log
|
51
|
+
elsif File.exist?(log)
|
52
|
+
@log = File.open(log, (File::WRONLY | File::APPEND))
|
53
|
+
@log.sync = true
|
54
|
+
else
|
55
|
+
FileUtils.mkdir_p(File.dirname(log))
|
56
|
+
@log = File.open(log, (File::WRONLY | File::APPEND | File::CREAT))
|
57
|
+
@log.sync = true
|
58
|
+
@log.write("# Logfile created on %s" % [Time.now.to_s])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_non_blocking_io
|
63
|
+
if !RUBY_PLATFORM.match(/java|mswin/) && !(@log == STDOUT) && @log.respond_to?(:write_nonblock)
|
64
|
+
@no_block = true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def add(severity, message = nil, progname = nil, &block)
|
69
|
+
from_framework = progname == IRONNAILS_FRAMEWORKNAME
|
70
|
+
fw_logging = defined?(IronNails::Logging::FRAMEWORK_LOGGING) && IronNails::Logging::FRAMEWORK_LOGGING
|
71
|
+
return if @level > severity || (from_framework && !fw_logging )
|
72
|
+
message = (message || (block && block.call) || progname).to_s
|
73
|
+
puts message if defined?(IronNails::Logging::CONSOLE_LOGGING) && IronNails::Logging::CONSOLE_LOGGING
|
74
|
+
# If a newline is necessary then create a new message ending with a newline.
|
75
|
+
# Ensures that the original message is not mutated.
|
76
|
+
message = "IRONNAILS: #{Severity.constants[severity]}: #{message}" if from_framework
|
77
|
+
message = "[#{Time.now.strftime("%d/%m/%Y %H:%M:%S.#{Time.now.usec}")}] #{message}\n" unless message[-1] == ?\n
|
78
|
+
buffer << message
|
79
|
+
auto_flush
|
80
|
+
message
|
81
|
+
end
|
82
|
+
|
83
|
+
for severity in Severity.constants
|
84
|
+
class_eval <<-EOT, __FILE__, __LINE__
|
85
|
+
def #{severity.downcase}(message = nil, progname = nil, &block)
|
86
|
+
add(#{severity}, message, progname, &block)
|
87
|
+
end
|
88
|
+
|
89
|
+
def #{severity.downcase}?
|
90
|
+
#{severity} >= @level
|
91
|
+
end
|
92
|
+
EOT
|
93
|
+
end
|
94
|
+
|
95
|
+
# Set the auto-flush period. Set to true to flush after every log message,
|
96
|
+
# to an integer to flush every N messages, or to false, nil, or zero to
|
97
|
+
# never auto-flush. If you turn auto-flushing off, be sure to regularly
|
98
|
+
# flush the log yourself -- it will eat up memory until you do.
|
99
|
+
def auto_flushing=(period)
|
100
|
+
@auto_flushing =
|
101
|
+
case period
|
102
|
+
when true;
|
103
|
+
1
|
104
|
+
when false, nil, 0;
|
105
|
+
MAX_BUFFER_SIZE
|
106
|
+
when Integer;
|
107
|
+
period
|
108
|
+
else
|
109
|
+
raise ArgumentError, "Unrecognized auto_flushing period: #{period.inspect}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def flush
|
114
|
+
unless buffer.empty?
|
115
|
+
if @no_block
|
116
|
+
@log.write_nonblock(buffer.slice!(0..-1).join)
|
117
|
+
else
|
118
|
+
@log.write(buffer.slice!(0..-1).join)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def close
|
124
|
+
flush
|
125
|
+
@log.close if @log.respond_to?(:close)
|
126
|
+
@log = nil
|
127
|
+
end
|
128
|
+
|
129
|
+
protected
|
130
|
+
def auto_flush
|
131
|
+
flush if buffer.size >= @auto_flushing
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module IronNails
|
2
|
+
|
3
|
+
module Logging
|
4
|
+
|
5
|
+
module ClassLogger
|
6
|
+
|
7
|
+
# provides access for the logger we are using
|
8
|
+
# you can override this logger as long as it responds to
|
9
|
+
# the methods: debug, info, warn, error, fatal
|
10
|
+
def logger
|
11
|
+
IRONNAILS_DEFAULT_LOGGER
|
12
|
+
end
|
13
|
+
|
14
|
+
# Ensures that a message is logged when the execution of
|
15
|
+
# the specified block throws an error. It will then re-raise the error.
|
16
|
+
def log_on_error
|
17
|
+
begin
|
18
|
+
yield if block_given?
|
19
|
+
rescue Exception => e
|
20
|
+
logger.error "IronNails Error: #{e}"
|
21
|
+
raise e
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module IronNails
|
2
|
+
|
3
|
+
module Models
|
4
|
+
|
5
|
+
module ModelMixin
|
6
|
+
|
7
|
+
include IronNails::Logging::ClassLogger
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Databinding
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
|
15
|
+
# defines a write-only attribute on an object
|
16
|
+
# this would map to a property setter in different languages
|
17
|
+
def attr_writer(*names)
|
18
|
+
names.each do |nm|
|
19
|
+
mn = nm
|
20
|
+
self.send :define_method, "#{nm}=".to_sym do |arg|
|
21
|
+
__vr__ = instance_variable_get :"@#{mn}"
|
22
|
+
return __vr__ if __vr__ == arg
|
23
|
+
instance_variable_set :"@#{mn}", arg
|
24
|
+
raise_property_changed mn
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# defines a read/write attribute on an object.
|
30
|
+
# this would map to a property with a getter and a setter in different langauages
|
31
|
+
def attr_accessor(*names)
|
32
|
+
attr_reader *names
|
33
|
+
attr_writer *names
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
# extend the class with the class methods defined in this module
|
40
|
+
def self.included(base)
|
41
|
+
base.send :include, System::ComponentModel::INotifyPropertyChanged unless base.ancestors.include? System::ComponentModel::INotifyPropertyChanged
|
42
|
+
base.extend ClassMethods
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_PropertyChanged(handler=nil)
|
46
|
+
@__handlers__ ||= []
|
47
|
+
@__handlers__ << handler
|
48
|
+
end
|
49
|
+
|
50
|
+
def remove_PropertyChanged(handler=nil)
|
51
|
+
@__handlers__ ||= []
|
52
|
+
@__handlers__.delete handler
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def raise_property_changed(name)
|
57
|
+
return unless @__handlers__
|
58
|
+
@__handlers__.each do |ev|
|
59
|
+
ev.invoke self, System::ComponentModel::PropertyChangedEventArgs.new(name) if ev.respond_to? :invoke
|
60
|
+
ev.call self, System::ComponentModel::PropertyChangedEventArgs.new(name) if ev.respond_to? :call
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,398 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/view/collections"
|
2
|
+
require File.dirname(__FILE__) + "/view/view_model"
|
3
|
+
require File.dirname(__FILE__) + "/view/xaml_proxy"
|
4
|
+
require File.dirname(__FILE__) + "/observable"
|
5
|
+
|
6
|
+
module IronNails
|
7
|
+
|
8
|
+
module Core
|
9
|
+
|
10
|
+
module ViewOperations
|
11
|
+
|
12
|
+
def init_view_operations
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_view(options)
|
16
|
+
logger.debug "View to load: #{options[:name]}", IRONNAILS_FRAMEWORKNAME
|
17
|
+
vw = IronNails::View::View.new(options)
|
18
|
+
#vw.add_observer(:loaded) { |sender| set_data_context_for(sender) }
|
19
|
+
vw
|
20
|
+
end
|
21
|
+
|
22
|
+
def register_child_view(options)
|
23
|
+
vw = registry.view_for options[:controller]
|
24
|
+
vw.add_child(options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def register_view_for(controller)
|
28
|
+
vw = build_view(:name => controller.view_name.to_sym, :controller => controller.controller_name)
|
29
|
+
vw.add_observer(:configuring) { |sender| configure_view(sender) }
|
30
|
+
registry.register_view_for controller, vw
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_view(controller, name = nil, &b)
|
34
|
+
find_view(controller, name).on_proxy(&b) #unless vw.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def from_view(controller, name, target, method)
|
38
|
+
find_view(controller, name).get_property(target, method)
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_update_ui_after(controller, options, &b)
|
42
|
+
# if options.is_a? Hash
|
43
|
+
# klass = options[:class]||BindableCollection
|
44
|
+
# request = options[:request]
|
45
|
+
# else
|
46
|
+
# klass = BindableCollection
|
47
|
+
# request = options
|
48
|
+
# end
|
49
|
+
# cb = System::Threading::WaitCallback.new do
|
50
|
+
# begin
|
51
|
+
# registry.view_for(controller).dispatcher.begin_invoke(DispatcherPriority.normal, Action.of(klass).new(&b), request.call)
|
52
|
+
# rescue WebException => e
|
53
|
+
# MessageBox.Show("There was a problem logging in to Twitter. #{e.message}");
|
54
|
+
# rescue RequestLimitException => e
|
55
|
+
# MessageBox.Show(e.message)
|
56
|
+
# rescue SecurityException => e
|
57
|
+
# MessageBox.Show("Incorrect username or password. Please try again");
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
# System::Threading::ThreadPool.queue_user_work_item cb
|
61
|
+
cb = nil
|
62
|
+
cb = options[:callback] unless options[:callback].nil?
|
63
|
+
options[:callback] = lambda do |vw|
|
64
|
+
b.call
|
65
|
+
refresh_view(vw)
|
66
|
+
end
|
67
|
+
find_view(controller, name).to_update_ui_after(options, &b)
|
68
|
+
end
|
69
|
+
|
70
|
+
def on_ui_thread(controller, options=nil, &b)
|
71
|
+
if options.is_a? Hash
|
72
|
+
data = options[:data]
|
73
|
+
klass = options[:class] || data.class
|
74
|
+
else
|
75
|
+
unless options.nil?
|
76
|
+
klass = options.class
|
77
|
+
data = options
|
78
|
+
end
|
79
|
+
end
|
80
|
+
b.call
|
81
|
+
#registry.view_for(controller).dispatcher.begin_invoke(DispatcherPriority.normal, options.nil? ? Action.new(&b) : Action.of(klass).new(&b), data)
|
82
|
+
end
|
83
|
+
|
84
|
+
alias_method :on_ui_thread_with, :on_ui_thread
|
85
|
+
|
86
|
+
def play_storyboard(controller, name, storyboard)
|
87
|
+
logger.debug "finding controller #{controller.controller_name} and view #{name} to play #{storyboard}"
|
88
|
+
vw = find_view(controller, name)
|
89
|
+
find_view(controller, name).play_storyboard(storyboard)
|
90
|
+
end
|
91
|
+
|
92
|
+
def stop_storyboard(controller, view_name, storyboard)
|
93
|
+
find_view(controller, name).stop_storyboard(storyboard)
|
94
|
+
end
|
95
|
+
|
96
|
+
def find_view(controller, name)
|
97
|
+
registry.view_for(controller).find(name)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
module ViewModelObjectOperations
|
102
|
+
|
103
|
+
# gets or sets the models that wil be used in the view to bind to
|
104
|
+
attr_accessor :model_queue
|
105
|
+
|
106
|
+
def init_object_operations
|
107
|
+
@model_queue = ModelCollection.new
|
108
|
+
end
|
109
|
+
|
110
|
+
# flags the view model as in need of wiring up and
|
111
|
+
# sets the model collection
|
112
|
+
def model_queue=(value)
|
113
|
+
unless model_queue == value
|
114
|
+
@configured = false
|
115
|
+
@model_queue = value
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# adds a new model to the queue for synchronisation to the view
|
120
|
+
def add_model_to_queue_on(model)
|
121
|
+
if model.respond_to?(:has_model?)
|
122
|
+
model.each do |m|
|
123
|
+
enqueue_model(m)
|
124
|
+
end
|
125
|
+
elsif model.is_a?(Hash)
|
126
|
+
enqueue_model(model)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
alias_method :add_models_to_queue_on, :add_model_to_queue_on
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def enqueue_model(model)
|
135
|
+
key = model.keys[0]
|
136
|
+
unless model_queue.has_model?(model) && model_queue[key] == model[key]
|
137
|
+
model_queue.add_model model
|
138
|
+
@configured = false
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
module ViewModelCommandOperations
|
145
|
+
|
146
|
+
# gets or sets the command_queue to respond to user actions in the view.
|
147
|
+
attr_accessor :command_queue
|
148
|
+
|
149
|
+
def init_command_operations
|
150
|
+
@command_queue = CommandCollection.new
|
151
|
+
end
|
152
|
+
|
153
|
+
# flags the view model as in need of wiring up and
|
154
|
+
# sets the command collection
|
155
|
+
def command_queue=(value)
|
156
|
+
unless command_queue == value
|
157
|
+
@configured = false
|
158
|
+
@command_queue = value
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# adds a command or a command collection to the queue
|
163
|
+
def add_command_to_queue(cmd)
|
164
|
+
|
165
|
+
if cmd.respond_to?(:has_command?)
|
166
|
+
cmd.each do |c|
|
167
|
+
enqueue_command(c)
|
168
|
+
end
|
169
|
+
elsif cmd.respond_to?(:execute) && cmd.respond_to?(:refresh_view) # define some sort of contract
|
170
|
+
enqueue_command(cmd)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
alias_method :add_commands_to_queue, :add_command_to_queue
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
def enqueue_command(cmd)
|
179
|
+
if !command_queue.has_command?(cmd) || cmd.changed?
|
180
|
+
cmd.add_observer(:refreshing_view) do |sender|
|
181
|
+
refresh_view(registry.view_for(sender.controller))
|
182
|
+
end
|
183
|
+
command_queue << cmd
|
184
|
+
@configured = false
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
module ViewModelOperations
|
191
|
+
|
192
|
+
# gets the view model instance to manipulate with this builder
|
193
|
+
attr_accessor :view_models
|
194
|
+
|
195
|
+
include ViewModelObjectOperations
|
196
|
+
include ViewModelCommandOperations
|
197
|
+
|
198
|
+
def init_viewmodel_operations
|
199
|
+
init_object_operations
|
200
|
+
init_command_operations
|
201
|
+
@view_models = {}
|
202
|
+
end
|
203
|
+
|
204
|
+
def register_viewmodel_for(controller)
|
205
|
+
vm_class_name = controller.view_model_name.camelize
|
206
|
+
Object.const_set vm_class_name, Class.new unless Object.const_defined? vm_class_name
|
207
|
+
|
208
|
+
# TODO: There is an issue with namespacing and CLR classes, they aren't registered as constants with
|
209
|
+
# IronRuby. This makes it hard to namespace viewmodels. If the namespace is included everything
|
210
|
+
# should work as normally. Will revisit this later to properly fix it.
|
211
|
+
vm_name = controller.view_model_name
|
212
|
+
klass = Object.const_get vm_class_name
|
213
|
+
klass.send :include, IronNails::Models::Databinding
|
214
|
+
klass.send :include, IronNails::View::ViewModelMixin
|
215
|
+
key = vm_name.to_sym
|
216
|
+
view_models[key] = klass.new if view_models[key].nil?
|
217
|
+
registry.register_viewmodel_for controller, view_models[key]
|
218
|
+
view_models[key]
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
class ComponentRegistryItem
|
224
|
+
|
225
|
+
attr_accessor :viewmodel
|
226
|
+
|
227
|
+
attr_accessor :view
|
228
|
+
|
229
|
+
def initialize(options={})
|
230
|
+
@view = options[:view]
|
231
|
+
@viewmodel = options[:viewmodel]
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
class ComponentRegistry
|
237
|
+
|
238
|
+
attr_accessor :components
|
239
|
+
|
240
|
+
def initialize
|
241
|
+
@components = {}
|
242
|
+
end
|
243
|
+
|
244
|
+
def register(controller)
|
245
|
+
components[controller.controller_name] = ComponentRegistryItem.new
|
246
|
+
end
|
247
|
+
|
248
|
+
def register_view_for(controller, view)
|
249
|
+
find_controller(controller).view = view
|
250
|
+
end
|
251
|
+
|
252
|
+
def register_viewmodel_for(controller, model)
|
253
|
+
find_controller(controller).viewmodel = model
|
254
|
+
end
|
255
|
+
|
256
|
+
def find_controller(controller)
|
257
|
+
con_name = controller.respond_to?(:controller_name) ? controller.controller_name : controller.to_sym
|
258
|
+
components[con_name]
|
259
|
+
end
|
260
|
+
|
261
|
+
def viewmodel_for(controller)
|
262
|
+
find_controller(controller).viewmodel
|
263
|
+
end
|
264
|
+
|
265
|
+
def view_for(controller)
|
266
|
+
find_controller(controller).view
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# This could be viewed as the life support for the Nails framework
|
271
|
+
# It serves as the glue between the different components.
|
272
|
+
# One of its main functions is to manage communication between the controller,
|
273
|
+
# view model and view.
|
274
|
+
class NailsEngine
|
275
|
+
|
276
|
+
include IronNails::Logging::ClassLogger
|
277
|
+
include ControllerObservable
|
278
|
+
include ViewOperations
|
279
|
+
include ViewModelOperations
|
280
|
+
|
281
|
+
# Stores the registered components and does lookup on them
|
282
|
+
attr_accessor :registry
|
283
|
+
|
284
|
+
def set_viewmodel_for(controller, key, value)
|
285
|
+
model = registry.viewmodel_for controller
|
286
|
+
model.set_model key, value
|
287
|
+
end
|
288
|
+
|
289
|
+
# configures the properties for the view model
|
290
|
+
def configure_models(model)
|
291
|
+
model_queue.each do |o|
|
292
|
+
o.each do |k, v|
|
293
|
+
model.add_model k, v
|
294
|
+
end unless o.nil?
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# processes the command queue.
|
299
|
+
def configure_events(model, view)
|
300
|
+
command_queue.each do |cmd|
|
301
|
+
case
|
302
|
+
when cmd.is_a?(EventCommand)
|
303
|
+
view.add_command(cmd)
|
304
|
+
when cmd.is_a?(TimedCommand)
|
305
|
+
view.add_timer(cmd)
|
306
|
+
view.proxy.start_timer(cmd)
|
307
|
+
when cmd.is_a?(BehaviorCommand)
|
308
|
+
model.add_command cmd
|
309
|
+
end unless cmd.attached?
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# configures the view
|
314
|
+
def configure_view(view)
|
315
|
+
model = registry.viewmodel_for view.controller
|
316
|
+
configure_models(model)
|
317
|
+
configure_events(model, view)
|
318
|
+
view.data_context = model unless view.has_datacontext? && !view.sets_datacontext?
|
319
|
+
@configured = true
|
320
|
+
end
|
321
|
+
|
322
|
+
# refreshes the data for the view.
|
323
|
+
def refresh_view(view)
|
324
|
+
notify_observers :refreshing_view, view.controller, self, view
|
325
|
+
view.configure
|
326
|
+
view.proxy.refresh
|
327
|
+
@configured = true
|
328
|
+
end
|
329
|
+
|
330
|
+
# synchronises the data in the viewmodel with the controller
|
331
|
+
def synchronise_with_controller
|
332
|
+
notify_observers :reading_input, self, view
|
333
|
+
end
|
334
|
+
|
335
|
+
def add_command_to_view(commands)
|
336
|
+
add_commands_to_queue commands
|
337
|
+
end
|
338
|
+
|
339
|
+
def synchronise_to_controller(controller)
|
340
|
+
objects = controller.instance_variable_get "@objects"
|
341
|
+
#model = registry.viewmodel_for controller #.objects.collect { |kvp| kvp.key.to_s.underscore.to_sym }
|
342
|
+
vw = registry.view_for controller
|
343
|
+
model = vw.proxy.data_context
|
344
|
+
objects.each do |k, v|
|
345
|
+
if model.respond_to?(k.to_sym)
|
346
|
+
val = model.send k.to_sym
|
347
|
+
objects[k] = val
|
348
|
+
controller.instance_variable_set "@#{k}", val
|
349
|
+
end
|
350
|
+
end
|
351
|
+
view_properties = controller.instance_variable_get "@view_properties"
|
352
|
+
view_properties.each do |k, v|
|
353
|
+
val = from_view controller, (v[:view]||controller.view_name), v[:element], v[:property]
|
354
|
+
instance_variable_set "@#{k}", val
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
# returns whether this view needs configuration or not
|
359
|
+
def configured?
|
360
|
+
!!@configured
|
361
|
+
end
|
362
|
+
|
363
|
+
def initialize
|
364
|
+
@configured, @registry = false, ComponentRegistry.new
|
365
|
+
init_viewmodel_operations
|
366
|
+
init_view_operations
|
367
|
+
end
|
368
|
+
|
369
|
+
def register_controller(controller)
|
370
|
+
logger.debug "registering controller #{controller.controller_name}", IRONNAILS_FRAMEWORKNAME
|
371
|
+
registry.register(controller)
|
372
|
+
register_viewmodel_for controller
|
373
|
+
register_view_for controller
|
374
|
+
controller.nails_engine = self
|
375
|
+
logger.debug "controller #{controller.controller_name} registered", IRONNAILS_FRAMEWORKNAME
|
376
|
+
end
|
377
|
+
|
378
|
+
def show_initial_window(controller)
|
379
|
+
logger.debug "setting up controller", IRONNAILS_FRAMEWORKNAME
|
380
|
+
#controller.setup_for_showing_view
|
381
|
+
registry.view_for(controller).load
|
382
|
+
controller.default_action if controller.respond_to? :default_action
|
383
|
+
controller.setup_for_showing_view
|
384
|
+
yield registry.view_for(controller).instance if block_given?
|
385
|
+
end
|
386
|
+
|
387
|
+
def initialize_with(command_definitions, models)
|
388
|
+
add_commands_to_queue command_definitions
|
389
|
+
add_models_to_queue_on models
|
390
|
+
logger.debug "Added commands to queue on view manager.", IRONNAILS_FRAMEWORKNAME
|
391
|
+
logger.debug "Added models to queue on view manager.", IRONNAILS_FRAMEWORKNAME
|
392
|
+
end
|
393
|
+
|
394
|
+
end
|
395
|
+
|
396
|
+
end
|
397
|
+
|
398
|
+
end
|