freightrain 0.5.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|