ironnails 0.0.1
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/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
|