freightrain 0.5.9
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/README +4 -0
- data/Rakefile +33 -0
- data/lib/extensions/gtk/dialog_helper.rb +50 -0
- data/lib/extensions/gtk/gtk_bootstrapper.rb +32 -0
- data/lib/extensions/gtk/interface_builder.rb +54 -0
- data/lib/extensions/gtk/widgets/gtk_calendar.rb +18 -0
- data/lib/extensions/gtk/widgets/gtk_combo_box.rb +41 -0
- data/lib/extensions/gtk/widgets/gtk_image.rb +14 -0
- data/lib/extensions/gtk/widgets/gtk_layout.rb +99 -0
- data/lib/extensions/gtk/widgets/gtk_toggle_button.rb +13 -0
- data/lib/extensions/gtk/widgets/gtk_toggle_tool_button.rb +11 -0
- data/lib/extensions/gtk/widgets/gtk_treeview.rb +80 -0
- data/lib/extensions/gtk/widgets/gtk_treeviewcolumn.rb +29 -0
- data/lib/extensions/gtk/widgets/gtk_widget.rb +47 -0
- data/lib/freightrain/auto_requirer.rb +24 -0
- data/lib/freightrain/binding/binding_host.rb +35 -0
- data/lib/freightrain/binding/binding_loader.rb +36 -0
- data/lib/freightrain/binding/converters/boolean_converter.rb +15 -0
- data/lib/freightrain/binding/converters/converter_factory.rb +22 -0
- data/lib/freightrain/binding/converters/default_converter.rb +16 -0
- data/lib/freightrain/binding/converters/float_converter.rb +16 -0
- data/lib/freightrain/binding/converters/integer_converter.rb +16 -0
- data/lib/freightrain/binding/freight_binding.rb +65 -0
- data/lib/freightrain/bootstrapper.rb +35 -0
- data/lib/freightrain/dialog_extension.rb +27 -0
- data/lib/freightrain/elements/element_extension.rb +1 -0
- data/lib/freightrain/elements/freight_element_view.rb +23 -0
- data/lib/freightrain/elements/freight_element_view_model.rb +42 -0
- data/lib/freightrain/elements/layout_widget.rb +11 -0
- data/lib/freightrain/freight_view.rb +52 -0
- data/lib/freightrain/freight_view_model.rb +45 -0
- data/lib/freightrain/interface_builder.rb +23 -0
- data/lib/freightrain/ioc/container.rb +21 -0
- data/lib/freightrain/ioc/container_hookable.rb +32 -0
- data/lib/freightrain/regions/freight_region.rb +32 -0
- data/lib/freightrain/regions/region_host.rb +37 -0
- data/lib/freightrain/services/freight_service.rb +14 -0
- data/lib/freightrain/services/service_host.rb +30 -0
- data/lib/freightrain/signals/freight_signal.rb +23 -0
- data/lib/freightrain/signals/signal_host.rb +38 -0
- data/lib/freightrain/string_patch.rb +24 -0
- data/lib/freightrain/testhelpers/container_helpers.rb +22 -0
- data/lib/freightrain/testhelpers.rb +2 -0
- data/lib/freightrain.rb +8 -0
- data/lib/scaffolding/commands/app.rb +29 -0
- data/lib/scaffolding/commands/triad.rb +29 -0
- data/lib/scaffolding/ftrain.rb +23 -0
- data/lib/scaffolding/generator.rb +15 -0
- data/lib/scaffolding/templates/application.ftt +11 -0
- data/lib/scaffolding/templates/view.ftt +5 -0
- data/lib/scaffolding/templates/view_bnd.ftt +0 -0
- data/lib/scaffolding/templates/view_control_glade.ftt +16 -0
- data/lib/scaffolding/templates/view_model.ftt +7 -0
- data/lib/scaffolding/templates/view_toplevel_glade.ftt +12 -0
- metadata +127 -0
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/clean'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/testtask'
|
9
|
+
|
10
|
+
spec = Gem::Specification.new do |s|
|
11
|
+
s.name = 'freightrain'
|
12
|
+
s.version = '0.5.9'
|
13
|
+
s.add_dependency('require_all', '>= 1.1.0')
|
14
|
+
s.add_dependency('needle', '>= 1.3.0')
|
15
|
+
s.has_rdoc = false
|
16
|
+
s.summary = 'ruby desktop development made easy'
|
17
|
+
s.description = s.summary
|
18
|
+
s.author = 'Andrea Dallera'
|
19
|
+
s.email = 'andrea@andreadallera.com'
|
20
|
+
s.files = %w(README Rakefile) + Dir.glob("{bin,lib}/**/*")
|
21
|
+
s.require_path = "lib"
|
22
|
+
end
|
23
|
+
|
24
|
+
Rake::GemPackageTask.new(spec) do |p|
|
25
|
+
p.gem_spec = spec
|
26
|
+
p.need_tar = true
|
27
|
+
p.need_zip = true
|
28
|
+
end
|
29
|
+
|
30
|
+
Spec::Rake::SpecTask.new do |t|
|
31
|
+
t.warning = true
|
32
|
+
t.spec_files = FileList['spec/**/*.rb']
|
33
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
module Freightrain
|
3
|
+
|
4
|
+
module Toolkit
|
5
|
+
|
6
|
+
module DialogHelper
|
7
|
+
|
8
|
+
def open_file_dialog(description = "Open file...")
|
9
|
+
raise "toplevel not implemented. If you want to use " +
|
10
|
+
"DialogHelper inside your class you should provide " +
|
11
|
+
"a toplevel widget via the toplevel method" unless respond_to? :toplevel
|
12
|
+
dialog = Gtk::FileChooserDialog.new(description,
|
13
|
+
toplevel,
|
14
|
+
Gtk::FileChooser::ACTION_OPEN,
|
15
|
+
nil,
|
16
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
|
17
|
+
[Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
|
18
|
+
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
|
19
|
+
result = dialog.filename
|
20
|
+
end
|
21
|
+
dialog.destroy
|
22
|
+
return result
|
23
|
+
end
|
24
|
+
|
25
|
+
def yes_no_dialog(description, title = "Warning!", secondary_text = "It will not be possible to recover the deleted item")
|
26
|
+
raise "toplevel not implemented. If you want to use " +
|
27
|
+
"DialogHelper inside your class you should provide " +
|
28
|
+
"a toplevel widget via the toplevel method" unless respond_to? :toplevel
|
29
|
+
result = false
|
30
|
+
dialog = Gtk::MessageDialog.new(
|
31
|
+
toplevel,
|
32
|
+
Gtk::Dialog::MODAL,
|
33
|
+
Gtk::MessageDialog::QUESTION,
|
34
|
+
Gtk::MessageDialog::BUTTONS_YES_NO,
|
35
|
+
description)
|
36
|
+
dialog.title = title
|
37
|
+
dialog.secondary_text = secondary_text
|
38
|
+
dialog.run do |response|
|
39
|
+
result = (response == Gtk::Dialog::RESPONSE_YES)
|
40
|
+
end
|
41
|
+
dialog.destroy
|
42
|
+
return result
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end #DialogHelper
|
47
|
+
|
48
|
+
end #Toolkit
|
49
|
+
|
50
|
+
end #Freightrain
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
#TODO: use require all
|
3
|
+
|
4
|
+
require 'libglade2'
|
5
|
+
require File.dirname(__FILE__) + "/widgets/gtk_image.rb"
|
6
|
+
require File.dirname(__FILE__) + "/widgets/gtk_toggle_button.rb"
|
7
|
+
require File.dirname(__FILE__) + "/widgets/gtk_toggle_tool_button.rb"
|
8
|
+
require File.dirname(__FILE__) + "/widgets/gtk_widget.rb"
|
9
|
+
require File.dirname(__FILE__) + "/widgets/gtk_combo_box.rb"
|
10
|
+
require File.dirname(__FILE__) + "/widgets/gtk_calendar.rb"
|
11
|
+
require File.dirname(__FILE__) + "/widgets/gtk_treeview.rb"
|
12
|
+
require File.dirname(__FILE__) + "/widgets/gtk_treeviewcolumn.rb"
|
13
|
+
require File.dirname(__FILE__) + "/widgets/gtk_layout.rb"
|
14
|
+
require File.dirname(__FILE__) + "/interface_builder.rb"
|
15
|
+
require File.dirname(__FILE__) + "/dialog_helper.rb"
|
16
|
+
|
17
|
+
module Freightrain
|
18
|
+
|
19
|
+
module Toolkit
|
20
|
+
|
21
|
+
def self.start_main_loop
|
22
|
+
yield
|
23
|
+
Gtk.main
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.quit
|
27
|
+
Gtk.main_quit
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
module Freightrain
|
3
|
+
|
4
|
+
module Toolkit
|
5
|
+
|
6
|
+
class InterfaceBuilder
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@builder = Gtk::Builder.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def file_found?(file_name)
|
13
|
+
return get_glade_file(file_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_objects_from_file(file_name)
|
17
|
+
@builder.add_from_file(get_glade_file(file_name))
|
18
|
+
@control = @builder.objects.first.toplevel
|
19
|
+
return @builder.objects.select do
|
20
|
+
|obj| obj.respond_to?(:name)
|
21
|
+
end.map
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_object_accessors(widgets, view)
|
25
|
+
widgets.each do |widget|
|
26
|
+
name = widget.name
|
27
|
+
view.instance_eval "def #{name}; @widgets.select { |w| w.name == '#{name}' }.first ;end;"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def connect_signals()
|
32
|
+
@builder.connect_signals do |signal|
|
33
|
+
yield(signal)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_glade_file(file_name)
|
38
|
+
search_path = File.join(
|
39
|
+
Freightrain.app_path,
|
40
|
+
"views",
|
41
|
+
"**",
|
42
|
+
file_name.to_convention + ".glade")
|
43
|
+
results = Dir.glob(search_path)
|
44
|
+
return results.first
|
45
|
+
end
|
46
|
+
|
47
|
+
def control
|
48
|
+
return @control
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
module Gtk
|
3
|
+
|
4
|
+
class ComboBox
|
5
|
+
|
6
|
+
def elements=(enumerable)
|
7
|
+
list_store = Gtk::ListStore.new(::Object, String)
|
8
|
+
enumerable.each do |item|
|
9
|
+
iterator = list_store.append
|
10
|
+
iterator[0] = item
|
11
|
+
iterator[1] = item.send(@text)
|
12
|
+
end
|
13
|
+
self.model = list_store
|
14
|
+
end
|
15
|
+
|
16
|
+
def selected
|
17
|
+
iter = self.active_iter
|
18
|
+
return iter[0] if iter
|
19
|
+
end
|
20
|
+
|
21
|
+
def selected=(object)
|
22
|
+
if self.model
|
23
|
+
self.model.each do |model, path, iter|
|
24
|
+
self.active_iter = iter if iter[0] == object
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def bind(options)
|
30
|
+
if options[:property].to_sym == :elements
|
31
|
+
@path = options[:path]
|
32
|
+
@text = options[:text] || "to_s"
|
33
|
+
renderer = Gtk::CellRendererText.new
|
34
|
+
self.pack_start(renderer, true)
|
35
|
+
self.add_attribute(renderer,:text, 1)
|
36
|
+
end
|
37
|
+
super(options)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
module Gtk
|
3
|
+
|
4
|
+
class Image
|
5
|
+
|
6
|
+
def image=(value)
|
7
|
+
file = File.new(File.join(Dir.tmpdir,"tempimg#{self.object_id.to_s}"),'w')
|
8
|
+
file << value
|
9
|
+
file.close
|
10
|
+
self.pixbuf = Gdk::Pixbuf.new(File.join(Dir.tmpdir,"tempimg#{self.object_id.to_s}"))
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
|
2
|
+
module Gtk
|
3
|
+
|
4
|
+
class Layout
|
5
|
+
include Freightrain::LayoutWidget
|
6
|
+
|
7
|
+
attr_writer :viewmodel
|
8
|
+
attr_reader :elements
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
def setup
|
16
|
+
@signals ||= {}
|
17
|
+
selected_callback = @signals[:selected]
|
18
|
+
@signals[:selected] = lambda do |value|
|
19
|
+
elements.each do |item|
|
20
|
+
item.set_selection(item.value == value)
|
21
|
+
end
|
22
|
+
selected_callback.call(value) if selected_callback
|
23
|
+
end
|
24
|
+
@elements ||= []
|
25
|
+
@ready = true
|
26
|
+
end
|
27
|
+
|
28
|
+
#TODO:remove
|
29
|
+
def default_display_logic(elements, enumerable)
|
30
|
+
#TODO: clean this mess
|
31
|
+
height = elements.length
|
32
|
+
delta = enumerable.length - elements.length
|
33
|
+
delta.abs.times do
|
34
|
+
if delta > 0
|
35
|
+
item = self.get_new_item
|
36
|
+
self.put(item.control, 0, item.control.height_request * height)
|
37
|
+
elements << item
|
38
|
+
height += 1
|
39
|
+
else
|
40
|
+
item = self.children.last
|
41
|
+
self.remove(item)
|
42
|
+
elements.delete_if {|element| element.control == item }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
height_factor = self.get_new_item.control.height_request
|
46
|
+
self.height = elements.length * height_factor
|
47
|
+
(0...elements.length).each do |index|
|
48
|
+
elements[index].value = enumerable[index]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def display_logic(enumerable)
|
53
|
+
if @display_logic
|
54
|
+
@display_logic.call(self, @elements, enumerable)
|
55
|
+
else
|
56
|
+
default_display_logic(@elements, enumerable)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def set_display_logic
|
61
|
+
@display_logic = lambda do |widget, elements, enumerable|
|
62
|
+
yield(widget, elements, enumerable)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def elements=(enumerable)
|
67
|
+
setup unless @ready
|
68
|
+
display_logic(enumerable)
|
69
|
+
end
|
70
|
+
|
71
|
+
def selected
|
72
|
+
return @elements.select { |element| element.selected == true}.first
|
73
|
+
end
|
74
|
+
|
75
|
+
def selected=(val)
|
76
|
+
@elements.each do |element|
|
77
|
+
element.set_selection(val == element.value)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def bind(options)
|
82
|
+
if options[:property].to_s == "elements"
|
83
|
+
@viewmodel = (options[:element].to_s + "_element_view_model").to_sym
|
84
|
+
options[:force] = true
|
85
|
+
end
|
86
|
+
super(options)
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_new_item
|
90
|
+
item = Freightrain[@viewmodel]
|
91
|
+
item.signals.each do |key, signal|
|
92
|
+
signal.connect(@signals[key]) if @signals[key]
|
93
|
+
end
|
94
|
+
return item
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
module Gtk
|
3
|
+
|
4
|
+
class TreeView
|
5
|
+
|
6
|
+
def create_column(column_definition)
|
7
|
+
renderer = Gtk::CellRendererText.new
|
8
|
+
title = column_definition.keys.first
|
9
|
+
attributes = {}
|
10
|
+
column_definition.values.first.each do |binding|
|
11
|
+
attributes[binding["property"]] = @paths[binding["path"]]
|
12
|
+
end
|
13
|
+
column = Gtk::TreeViewColumn.new(title, renderer, attributes)
|
14
|
+
self.append_column column
|
15
|
+
end
|
16
|
+
|
17
|
+
def bind(options)
|
18
|
+
if options[:property].to_sym == :elements
|
19
|
+
columns = normalize_column_bindings(options[:columns])
|
20
|
+
@paths = get_paths(columns)
|
21
|
+
columns.each do |column|
|
22
|
+
create_column(column)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
super(options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def elements=(enumerable)
|
29
|
+
types = [::Object]
|
30
|
+
@paths.length.times do
|
31
|
+
types << String #TODO: allow for different types
|
32
|
+
end
|
33
|
+
list_store = Gtk::ListStore.new(*types)
|
34
|
+
enumerable.each do |item|
|
35
|
+
iterator = list_store.append
|
36
|
+
iterator[0] = item
|
37
|
+
@paths.each do |key, index|
|
38
|
+
iterator[index] = item.send(key).to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
self.model = list_store
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def normalize_column_bindings(columns)
|
46
|
+
result = []
|
47
|
+
#step 1 : turn name/values only into arrays with 1 path
|
48
|
+
columns.each do |col|
|
49
|
+
unless col.values.first.kind_of?(Array)
|
50
|
+
result << {col.keys.first => [{"path" => col.values.first}]}
|
51
|
+
else
|
52
|
+
result << col
|
53
|
+
end
|
54
|
+
end
|
55
|
+
#step 2 : add property key if not present
|
56
|
+
result.each do |col|
|
57
|
+
col.values.first.each do |bindings|
|
58
|
+
unless bindings["property"]
|
59
|
+
bindings["property"] = "text"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
return result
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_paths(columns)
|
67
|
+
paths = {}
|
68
|
+
columns.each do |column|
|
69
|
+
column.values.each do |binding|
|
70
|
+
binding.each do |definition|
|
71
|
+
paths[definition["path"]] = paths.length + 1 unless paths[definition["path"]]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
return paths
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Gtk
|
4
|
+
|
5
|
+
class TreeViewColumn
|
6
|
+
|
7
|
+
attr_accessor :path
|
8
|
+
|
9
|
+
attr_writer :property
|
10
|
+
attr_writer :type
|
11
|
+
|
12
|
+
def type
|
13
|
+
return @type if @type
|
14
|
+
return String
|
15
|
+
end
|
16
|
+
|
17
|
+
def property
|
18
|
+
return @property if @property
|
19
|
+
return :text
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
module Gtk
|
3
|
+
|
4
|
+
class Widget
|
5
|
+
|
6
|
+
def bind(binding_options)
|
7
|
+
@bindings ||= {}
|
8
|
+
binding = FreightBinding.new(self, binding_options)
|
9
|
+
#TODO: send out warning on override?
|
10
|
+
@bindings[binding_options[:property]] = binding
|
11
|
+
return binding
|
12
|
+
end
|
13
|
+
|
14
|
+
def bindings
|
15
|
+
@bindings ||= {}
|
16
|
+
return @bindings.values
|
17
|
+
end
|
18
|
+
|
19
|
+
def color_from_rgb(string)
|
20
|
+
red = ((string[0..1].hex.to_i + 1) * 256) - 1
|
21
|
+
green = ((string[2..3].hex.to_i + 1) * 256) - 1
|
22
|
+
blue = ((string[4..5].hex.to_i + 1) * 256) - 1
|
23
|
+
return Gdk::Color.new(red, green, blue)
|
24
|
+
end
|
25
|
+
|
26
|
+
def background=(color)
|
27
|
+
self.modify_bg(Gtk::STATE_NORMAL, color_from_rgb(color))
|
28
|
+
end
|
29
|
+
|
30
|
+
def foreground=(color)
|
31
|
+
self.modify_fg(Gtk::STATE_NORMAL, color_from_rgb(color))
|
32
|
+
end
|
33
|
+
|
34
|
+
def base_background=(color)
|
35
|
+
self.modify_base(Gtk::STATE_NORMAL, color_from_rgb(color))
|
36
|
+
end
|
37
|
+
|
38
|
+
def plug_in(region_widget)
|
39
|
+
self.remove(self.children.first) unless self.children.empty?
|
40
|
+
self << region_widget
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
module Freightrain
|
3
|
+
|
4
|
+
def self.auto_require!
|
5
|
+
|
6
|
+
views_folder = self.views_path
|
7
|
+
viewmodels_folder = File.join(self.app_path, "viewmodels")
|
8
|
+
services_folder = File.join(self.app_path, "services")
|
9
|
+
domain_folder = File.join(self.app_path, "domain")
|
10
|
+
|
11
|
+
require_all views_folder
|
12
|
+
require_all viewmodels_folder
|
13
|
+
|
14
|
+
if File.directory?(services_folder) && Dir.entries(services_folder).length > 2
|
15
|
+
require_all services_folder
|
16
|
+
end
|
17
|
+
|
18
|
+
if File.directory?(domain_folder) && Dir.entries(domain_folder).length > 2
|
19
|
+
require_all domain_folder
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
module Freightrain
|
3
|
+
|
4
|
+
module BindingHost
|
5
|
+
|
6
|
+
def load_bindings_from_file(widgets)
|
7
|
+
loader = BindingLoader.new(self.class.name)
|
8
|
+
loader.each_binding do |widget_name, options|
|
9
|
+
widget = widgets.select { |widget| widget.name == widget_name }.first
|
10
|
+
widget.bind(options) if widget
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def data_source=(source)
|
15
|
+
bindings.each do |binding|
|
16
|
+
binding.data_source = source
|
17
|
+
binding.update
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def bindings
|
22
|
+
return widgets.each.map { |widget| widget.bindings }.flatten
|
23
|
+
end
|
24
|
+
|
25
|
+
def commit
|
26
|
+
bindings.each { |binding| binding.commit }
|
27
|
+
end
|
28
|
+
|
29
|
+
def update
|
30
|
+
bindings.each { |binding| binding.update}
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Freightrain
|
5
|
+
|
6
|
+
class BindingLoader
|
7
|
+
|
8
|
+
def initialize(class_name)
|
9
|
+
@search_path = File.join(
|
10
|
+
Freightrain.views_path,
|
11
|
+
"**",
|
12
|
+
class_name.to_convention + ".bnd.yml")
|
13
|
+
end
|
14
|
+
|
15
|
+
def filename
|
16
|
+
result = Dir.glob(@search_path)
|
17
|
+
return result[0] || nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def each_binding
|
21
|
+
return nil unless filename
|
22
|
+
binding_collection = YAML.load_file(filename) || []
|
23
|
+
binding_collection.each do |widget, binding|
|
24
|
+
binding = [binding] if binding.kind_of? Hash #needed if only 1 binding declared
|
25
|
+
binding.each do |binding_options|
|
26
|
+
options = {}
|
27
|
+
binding_options.each do |key, value|
|
28
|
+
options[key.to_sym] = value
|
29
|
+
end
|
30
|
+
yield(widget, options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
module Freightrain
|
3
|
+
|
4
|
+
class ConverterFactory
|
5
|
+
|
6
|
+
def self.create(converter_name)
|
7
|
+
name = converter_name.to_s.capitalize
|
8
|
+
#first try to get one of the standard converters
|
9
|
+
if Freightrain.const_defined? "#{name}Converter"
|
10
|
+
return Freightrain.const_get("#{name}Converter").new
|
11
|
+
end
|
12
|
+
#then, if not found, try to get a registered custom converter
|
13
|
+
begin
|
14
|
+
return Freightrain["#{name}Converter".to_convention_sym]
|
15
|
+
rescue
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|