rui 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +4 -0
- data/COPYING +165 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +24 -0
- data/README.md +8 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/examples/autogui/main.rb +12 -0
- data/examples/hello/main.rb +13 -0
- data/examples/kderun.rb +4 -0
- data/examples/qtrun.rb +4 -0
- data/examples/signals/main.rb +15 -0
- data/lib/rui/descriptor.rb +241 -0
- data/lib/rui/factory.rb +84 -0
- data/lib/rui/observer_utils.rb +178 -0
- data/lib/rui/toolkits/kde/kde.rb +276 -0
- data/lib/rui/toolkits/qt/qt.rb +350 -0
- data/lib/rui/toolkits/qtbase/gui_builder.rb +344 -0
- data/lib/rui/toolkits/qtbase/qt.rb +555 -0
- data/lib/rui/utils.rb +42 -0
- data/lib/rui.rb +54 -0
- data/rui.gemspec +92 -0
- data/test/helper.rb +17 -0
- data/test/test_descriptor.rb +237 -0
- data/test/test_factory.rb +38 -0
- data/test/test_observer_utils.rb +67 -0
- metadata +189 -0
data/.document
ADDED
data/COPYING
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
GNU LESSER GENERAL PUBLIC LICENSE
|
2
|
+
Version 3, 29 June 2007
|
3
|
+
|
4
|
+
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
5
|
+
Everyone is permitted to copy and distribute verbatim copies
|
6
|
+
of this license document, but changing it is not allowed.
|
7
|
+
|
8
|
+
|
9
|
+
This version of the GNU Lesser General Public License incorporates
|
10
|
+
the terms and conditions of version 3 of the GNU General Public
|
11
|
+
License, supplemented by the additional permissions listed below.
|
12
|
+
|
13
|
+
0. Additional Definitions.
|
14
|
+
|
15
|
+
As used herein, "this License" refers to version 3 of the GNU Lesser
|
16
|
+
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
17
|
+
General Public License.
|
18
|
+
|
19
|
+
"The Library" refers to a covered work governed by this License,
|
20
|
+
other than an Application or a Combined Work as defined below.
|
21
|
+
|
22
|
+
An "Application" is any work that makes use of an interface provided
|
23
|
+
by the Library, but which is not otherwise based on the Library.
|
24
|
+
Defining a subclass of a class defined by the Library is deemed a mode
|
25
|
+
of using an interface provided by the Library.
|
26
|
+
|
27
|
+
A "Combined Work" is a work produced by combining or linking an
|
28
|
+
Application with the Library. The particular version of the Library
|
29
|
+
with which the Combined Work was made is also called the "Linked
|
30
|
+
Version".
|
31
|
+
|
32
|
+
The "Minimal Corresponding Source" for a Combined Work means the
|
33
|
+
Corresponding Source for the Combined Work, excluding any source code
|
34
|
+
for portions of the Combined Work that, considered in isolation, are
|
35
|
+
based on the Application, and not on the Linked Version.
|
36
|
+
|
37
|
+
The "Corresponding Application Code" for a Combined Work means the
|
38
|
+
object code and/or source code for the Application, including any data
|
39
|
+
and utility programs needed for reproducing the Combined Work from the
|
40
|
+
Application, but excluding the System Libraries of the Combined Work.
|
41
|
+
|
42
|
+
1. Exception to Section 3 of the GNU GPL.
|
43
|
+
|
44
|
+
You may convey a covered work under sections 3 and 4 of this License
|
45
|
+
without being bound by section 3 of the GNU GPL.
|
46
|
+
|
47
|
+
2. Conveying Modified Versions.
|
48
|
+
|
49
|
+
If you modify a copy of the Library, and, in your modifications, a
|
50
|
+
facility refers to a function or data to be supplied by an Application
|
51
|
+
that uses the facility (other than as an argument passed when the
|
52
|
+
facility is invoked), then you may convey a copy of the modified
|
53
|
+
version:
|
54
|
+
|
55
|
+
a) under this License, provided that you make a good faith effort to
|
56
|
+
ensure that, in the event an Application does not supply the
|
57
|
+
function or data, the facility still operates, and performs
|
58
|
+
whatever part of its purpose remains meaningful, or
|
59
|
+
|
60
|
+
b) under the GNU GPL, with none of the additional permissions of
|
61
|
+
this License applicable to that copy.
|
62
|
+
|
63
|
+
3. Object Code Incorporating Material from Library Header Files.
|
64
|
+
|
65
|
+
The object code form of an Application may incorporate material from
|
66
|
+
a header file that is part of the Library. You may convey such object
|
67
|
+
code under terms of your choice, provided that, if the incorporated
|
68
|
+
material is not limited to numerical parameters, data structure
|
69
|
+
layouts and accessors, or small macros, inline functions and templates
|
70
|
+
(ten or fewer lines in length), you do both of the following:
|
71
|
+
|
72
|
+
a) Give prominent notice with each copy of the object code that the
|
73
|
+
Library is used in it and that the Library and its use are
|
74
|
+
covered by this License.
|
75
|
+
|
76
|
+
b) Accompany the object code with a copy of the GNU GPL and this license
|
77
|
+
document.
|
78
|
+
|
79
|
+
4. Combined Works.
|
80
|
+
|
81
|
+
You may convey a Combined Work under terms of your choice that,
|
82
|
+
taken together, effectively do not restrict modification of the
|
83
|
+
portions of the Library contained in the Combined Work and reverse
|
84
|
+
engineering for debugging such modifications, if you also do each of
|
85
|
+
the following:
|
86
|
+
|
87
|
+
a) Give prominent notice with each copy of the Combined Work that
|
88
|
+
the Library is used in it and that the Library and its use are
|
89
|
+
covered by this License.
|
90
|
+
|
91
|
+
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
92
|
+
document.
|
93
|
+
|
94
|
+
c) For a Combined Work that displays copyright notices during
|
95
|
+
execution, include the copyright notice for the Library among
|
96
|
+
these notices, as well as a reference directing the user to the
|
97
|
+
copies of the GNU GPL and this license document.
|
98
|
+
|
99
|
+
d) Do one of the following:
|
100
|
+
|
101
|
+
0) Convey the Minimal Corresponding Source under the terms of this
|
102
|
+
License, and the Corresponding Application Code in a form
|
103
|
+
suitable for, and under terms that permit, the user to
|
104
|
+
recombine or relink the Application with a modified version of
|
105
|
+
the Linked Version to produce a modified Combined Work, in the
|
106
|
+
manner specified by section 6 of the GNU GPL for conveying
|
107
|
+
Corresponding Source.
|
108
|
+
|
109
|
+
1) Use a suitable shared library mechanism for linking with the
|
110
|
+
Library. A suitable mechanism is one that (a) uses at run time
|
111
|
+
a copy of the Library already present on the user's computer
|
112
|
+
system, and (b) will operate properly with a modified version
|
113
|
+
of the Library that is interface-compatible with the Linked
|
114
|
+
Version.
|
115
|
+
|
116
|
+
e) Provide Installation Information, but only if you would otherwise
|
117
|
+
be required to provide such information under section 6 of the
|
118
|
+
GNU GPL, and only to the extent that such information is
|
119
|
+
necessary to install and execute a modified version of the
|
120
|
+
Combined Work produced by recombining or relinking the
|
121
|
+
Application with a modified version of the Linked Version. (If
|
122
|
+
you use option 4d0, the Installation Information must accompany
|
123
|
+
the Minimal Corresponding Source and Corresponding Application
|
124
|
+
Code. If you use option 4d1, you must provide the Installation
|
125
|
+
Information in the manner specified by section 6 of the GNU GPL
|
126
|
+
for conveying Corresponding Source.)
|
127
|
+
|
128
|
+
5. Combined Libraries.
|
129
|
+
|
130
|
+
You may place library facilities that are a work based on the
|
131
|
+
Library side by side in a single library together with other library
|
132
|
+
facilities that are not Applications and are not covered by this
|
133
|
+
License, and convey such a combined library under terms of your
|
134
|
+
choice, if you do both of the following:
|
135
|
+
|
136
|
+
a) Accompany the combined library with a copy of the same work based
|
137
|
+
on the Library, uncombined with any other library facilities,
|
138
|
+
conveyed under the terms of this License.
|
139
|
+
|
140
|
+
b) Give prominent notice with the combined library that part of it
|
141
|
+
is a work based on the Library, and explaining where to find the
|
142
|
+
accompanying uncombined form of the same work.
|
143
|
+
|
144
|
+
6. Revised Versions of the GNU Lesser General Public License.
|
145
|
+
|
146
|
+
The Free Software Foundation may publish revised and/or new versions
|
147
|
+
of the GNU Lesser General Public License from time to time. Such new
|
148
|
+
versions will be similar in spirit to the present version, but may
|
149
|
+
differ in detail to address new problems or concerns.
|
150
|
+
|
151
|
+
Each version is given a distinguishing version number. If the
|
152
|
+
Library as you received it specifies that a certain numbered version
|
153
|
+
of the GNU Lesser General Public License "or any later version"
|
154
|
+
applies to it, you have the option of following the terms and
|
155
|
+
conditions either of that published version or of any later version
|
156
|
+
published by the Free Software Foundation. If the Library as you
|
157
|
+
received it does not specify a version number of the GNU Lesser
|
158
|
+
General Public License, you may choose any version of the GNU Lesser
|
159
|
+
General Public License ever published by the Free Software Foundation.
|
160
|
+
|
161
|
+
If the Library as you received it specifies that a proxy can decide
|
162
|
+
whether future versions of the GNU Lesser General Public License shall
|
163
|
+
apply, that proxy's public statement of acceptance of any version is
|
164
|
+
permanent authorization for you to choose that version for the
|
165
|
+
Library.
|
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
gem "builder"
|
6
|
+
|
7
|
+
# Add dependencies to develop your gem here.
|
8
|
+
# Include everything needed to run rake, tests, features, etc.
|
9
|
+
group :development do
|
10
|
+
gem "yard", "~> 0.6.0"
|
11
|
+
gem "bluecloth"
|
12
|
+
gem "bundler", "~> 1.0.0"
|
13
|
+
gem "jeweler", "~> 1.5.1"
|
14
|
+
gem "rcov", ">= 0"
|
15
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
bluecloth (2.0.9)
|
5
|
+
builder (3.0.0)
|
6
|
+
git (1.2.5)
|
7
|
+
jeweler (1.5.1)
|
8
|
+
bundler (~> 1.0.0)
|
9
|
+
git (>= 1.2.5)
|
10
|
+
rake
|
11
|
+
rake (0.8.7)
|
12
|
+
rcov (0.9.9)
|
13
|
+
yard (0.6.3)
|
14
|
+
|
15
|
+
PLATFORMS
|
16
|
+
ruby
|
17
|
+
|
18
|
+
DEPENDENCIES
|
19
|
+
bluecloth
|
20
|
+
builder
|
21
|
+
bundler (~> 1.0.0)
|
22
|
+
jeweler (~> 1.5.1)
|
23
|
+
rcov
|
24
|
+
yard (~> 0.6.0)
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
+
gem.name = "rui"
|
16
|
+
gem.homepage = "http://github.com/pcapriotti/rui"
|
17
|
+
gem.license = "LGPL"
|
18
|
+
gem.description = %q{GUI abstraction library supporting Qt and KDE backends}
|
19
|
+
gem.summary = %q{GUI abstraction library}
|
20
|
+
gem.email = "p.capriotti@gmail.com"
|
21
|
+
gem.authors = ["Paolo Capriotti"]
|
22
|
+
gem.rubyforge_project = "nowarning"
|
23
|
+
end
|
24
|
+
Jeweler::RubygemsDotOrgTasks.new
|
25
|
+
|
26
|
+
require 'rake/testtask'
|
27
|
+
Rake::TestTask.new(:test) do |test|
|
28
|
+
test.libs << 'lib' << 'test'
|
29
|
+
test.pattern = 'test/**/test_*.rb'
|
30
|
+
test.verbose = true
|
31
|
+
end
|
32
|
+
|
33
|
+
require 'rcov/rcovtask'
|
34
|
+
Rcov::RcovTask.new do |test|
|
35
|
+
test.libs << 'test'
|
36
|
+
test.pattern = 'test/**/test_*.rb'
|
37
|
+
test.verbose = true
|
38
|
+
end
|
39
|
+
|
40
|
+
task :default => :test
|
41
|
+
|
42
|
+
require 'yard'
|
43
|
+
YARD::Rake::YardocTask.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rui'
|
2
|
+
|
3
|
+
RUI::Application.init('autogui') do |app|
|
4
|
+
widget = Qt::Widget.new
|
5
|
+
widget.gui = RUI::autogui do
|
6
|
+
layout(:type => :vertical) do
|
7
|
+
button(:name => :quit, :text => "Quit")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
widget.quit.on(:clicked) { app.exit }
|
11
|
+
widget.show
|
12
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Copyright (c) 2010 Paolo Capriotti <p.capriotti@gmail.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU Lesser General Public License as
|
5
|
+
# published by the Free Software Foundation; either version 3 of the
|
6
|
+
# License, or (at your option) any later version.
|
7
|
+
|
8
|
+
require 'rui'
|
9
|
+
|
10
|
+
RUI::Application.init('hello') do
|
11
|
+
RUI::PushButton.new("Hello").show
|
12
|
+
end
|
13
|
+
|
data/examples/kderun.rb
ADDED
data/examples/qtrun.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Copyright (c) 2010 Paolo Capriotti <p.capriotti@gmail.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU Lesser General Public License as
|
5
|
+
# published by the Free Software Foundation; either version 3 of the
|
6
|
+
# License, or (at your option) any later version.
|
7
|
+
|
8
|
+
require 'rui'
|
9
|
+
|
10
|
+
RUI::Application.init('signals') do |app|
|
11
|
+
button = RUI::PushButton.new("Quit")
|
12
|
+
button.on(:clicked) { app.exit }
|
13
|
+
button.show
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,241 @@
|
|
1
|
+
# Copyright (c) 2009 Paolo Capriotti <p.capriotti@gmail.com>
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation; either version 2 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
|
8
|
+
#
|
9
|
+
# A <b>descriptor</b> is a rose tree with arbitrary properties at each node, used to
|
10
|
+
# define GUIs declaratively.
|
11
|
+
#
|
12
|
+
# Descriptors can be created using a DSL. For example:
|
13
|
+
#
|
14
|
+
# ex1 = Descriptor.build(:root, :name => 'parent') do
|
15
|
+
# child :name => 'foo'
|
16
|
+
# child :name => 'bar'
|
17
|
+
# merge_point
|
18
|
+
# child :name => 'hello' do
|
19
|
+
# grandchild :name => 'world'
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# creates a tree which has a node with no name and three children with names
|
24
|
+
# 'foo', 'bar', and 'hello', and hello having a child of its own, called
|
25
|
+
# 'world'. Note that <b>descriptor tags</b> (<tt>:root</tt>, <tt>:child</tt> and
|
26
|
+
# <tt>:grandchild</tt> in the example) are completely arbitrary, but they play
|
27
|
+
# a special role when merging, together with the <tt>:name</tt> property.
|
28
|
+
#
|
29
|
+
# <b>Merging</b> consists of taking two descriptor trees, and matching their roots by
|
30
|
+
# tag and name. If they match, their children are recursively matched and
|
31
|
+
# merged, or simply concatenated when no match is found.
|
32
|
+
#
|
33
|
+
# For example, if <tt>ex1</tt> above is merged with the following descriptor:
|
34
|
+
#
|
35
|
+
# ex2 = Descriptor.build(:root, :name => 'parent') do
|
36
|
+
# child :name => 'foo2'
|
37
|
+
# child :name => 'hello' do
|
38
|
+
# grandchild :name => 'world2'
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# the resulting descriptor would be equivalent to the one created by:
|
43
|
+
#
|
44
|
+
# ex1_merged_with_ex2 = Descriptor.build(:root, :name => 'parent') do
|
45
|
+
# child :name => 'foo'
|
46
|
+
# child :name => 'bar'
|
47
|
+
# child :name => 'foo2'
|
48
|
+
# child :name => 'hello' do
|
49
|
+
# grandchild :name => 'world'
|
50
|
+
# grandchild :name => 'world2'
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# As can be seen in the example, <b>merge points</b> can be used to specify exactly
|
55
|
+
# where children of merged descriptors should be inserted.
|
56
|
+
#
|
57
|
+
# Merge points can optionally have a <tt>count</tt>, which specifies the number
|
58
|
+
# of children to be inserted on that particular point. When the count is
|
59
|
+
# satisfied, additional children are added at the following merge point, or, if
|
60
|
+
# no more merge points exist, at the bottom.
|
61
|
+
#
|
62
|
+
class Descriptor
|
63
|
+
attr_reader :tag # @return [Symbol] the descriptor tag
|
64
|
+
attr_reader :opts # @return [Hash] properties for this descriptor
|
65
|
+
attr_reader :children # @return [Array] children of this descriptor
|
66
|
+
|
67
|
+
#
|
68
|
+
# Create a descriptor using the DSL.
|
69
|
+
# @param tag [Symbol] descriptor tag
|
70
|
+
# @param opts [Hash] arbitrary hash of properties
|
71
|
+
# @return [Descriptor]
|
72
|
+
#
|
73
|
+
def self.build(tag, opts = { }, &blk)
|
74
|
+
root = new(tag, opts)
|
75
|
+
builder = Builder.new(root)
|
76
|
+
builder.instance_eval(&blk) if block_given?
|
77
|
+
root
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Create a descriptor with no children.
|
82
|
+
# @param tag [Symbol] descriptor tag
|
83
|
+
# @param opts [Hash] arbitrary hash of properties
|
84
|
+
#
|
85
|
+
def initialize(tag, opts = { })
|
86
|
+
@tag = tag
|
87
|
+
@opts = opts
|
88
|
+
@children = []
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# Add a child to this descriptor.
|
93
|
+
#
|
94
|
+
def add_child(desc)
|
95
|
+
@children << desc
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
# Add a child to this descriptor, taking merge points into account.
|
100
|
+
#
|
101
|
+
def merge_child(desc)
|
102
|
+
mp = @opts[:merge_points].first if @opts[:merge_points]
|
103
|
+
if mp
|
104
|
+
@children.insert(mp.position, desc)
|
105
|
+
@opts[:merge_points].step!
|
106
|
+
else
|
107
|
+
add_child(desc)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Add a merge point to this descriptor. Newly added merge points will not
|
113
|
+
# affect existing children, even if they were added with <tt>merge_child</tt>
|
114
|
+
# @param position merge point position
|
115
|
+
# @param count maximum number of children that can be merged at this point.
|
116
|
+
# If negative, no limit on the number of mergeable children is set.
|
117
|
+
#
|
118
|
+
def add_merge_point(position, count = -1)
|
119
|
+
mp = MergePoint.new(position, count)
|
120
|
+
@opts[:merge_points] ||= MergePoint::List.new
|
121
|
+
@opts[:merge_points].add(mp)
|
122
|
+
mp
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Convert this descriptor to a human readable sexp representation. Descriptor
|
127
|
+
# properties are printed as ruby hashes.
|
128
|
+
#
|
129
|
+
def to_sexp
|
130
|
+
"(#{@tag} #{@opts.inspect}#{@children.map{|c| ' ' + c.to_sexp}.join})"
|
131
|
+
end
|
132
|
+
|
133
|
+
#
|
134
|
+
# Destructively merge this descriptor with another.
|
135
|
+
#
|
136
|
+
# Descriptors are merged if they match by tag and name, or if this descriptor
|
137
|
+
# has tag <tt>:group</tt> and the other one has a property <tt>:group</tt>
|
138
|
+
# set to the name of this descriptor.
|
139
|
+
#
|
140
|
+
# @param other the descriptor to be merged
|
141
|
+
# @return [Boolean] whether the merge was successful
|
142
|
+
#
|
143
|
+
def merge!(other)
|
144
|
+
if tag == other.tag and
|
145
|
+
opts[:name] == other.opts[:name]
|
146
|
+
# if roots match
|
147
|
+
other.children.each do |child2|
|
148
|
+
# merge each of the children of the second descriptor
|
149
|
+
merged = false
|
150
|
+
children.each do |child|
|
151
|
+
# try to match with any of the children of the first descriptor
|
152
|
+
if child.merge!(child2)
|
153
|
+
merged = true
|
154
|
+
break
|
155
|
+
end
|
156
|
+
end
|
157
|
+
# if no match is found, just add it as a child of the root
|
158
|
+
merge_child(child2.dup) unless merged
|
159
|
+
end
|
160
|
+
true
|
161
|
+
elsif tag == :group and other.opts[:group] == opts[:name]
|
162
|
+
# if the root is the group of the second descriptor, add it as a child
|
163
|
+
merge_child(other)
|
164
|
+
else
|
165
|
+
false
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class MergePoint
|
170
|
+
attr_accessor :position, :count
|
171
|
+
|
172
|
+
class List
|
173
|
+
def initialize
|
174
|
+
@mps = []
|
175
|
+
end
|
176
|
+
|
177
|
+
def first
|
178
|
+
@mps.first
|
179
|
+
end
|
180
|
+
|
181
|
+
def add(mp)
|
182
|
+
@mps << mp
|
183
|
+
end
|
184
|
+
|
185
|
+
def step!
|
186
|
+
raise "Stepping invalid merge point list" if @mps.empty?
|
187
|
+
@mps.each do |mp|
|
188
|
+
mp.position += 1
|
189
|
+
end
|
190
|
+
@mps.first.count -= 1
|
191
|
+
clean!
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
def clean!
|
197
|
+
@mps.delete_if {|mp| not mp.valid? }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def initialize(position, count = -1)
|
202
|
+
@position = position
|
203
|
+
@count = count
|
204
|
+
raise "Creating invalid merge point" if @count == 0
|
205
|
+
end
|
206
|
+
|
207
|
+
def valid?
|
208
|
+
@count != 0
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
class Builder
|
213
|
+
attr_reader :__desc__
|
214
|
+
private :__desc__
|
215
|
+
|
216
|
+
def initialize(desc)
|
217
|
+
@__desc__ = desc
|
218
|
+
end
|
219
|
+
|
220
|
+
def method_missing(name, *args, &blk)
|
221
|
+
opts = if args.empty?
|
222
|
+
{ }
|
223
|
+
elsif args.size == 1
|
224
|
+
if args.first.is_a? Hash
|
225
|
+
args.first
|
226
|
+
else
|
227
|
+
{ :name => args.first }
|
228
|
+
end
|
229
|
+
else
|
230
|
+
args[-1].merge(:name => args.first)
|
231
|
+
end
|
232
|
+
child = Descriptor.new(name, opts)
|
233
|
+
self.class.new(child).instance_eval(&blk) if block_given?
|
234
|
+
__desc__.add_child(child)
|
235
|
+
end
|
236
|
+
|
237
|
+
def merge_point(count = -1)
|
238
|
+
@__desc__.add_merge_point(@__desc__.children.size, count)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
data/lib/rui/factory.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# Copyright (c) 2009 Paolo Capriotti <p.capriotti@gmail.com>
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation; either version 2 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
|
8
|
+
class Proc
|
9
|
+
#
|
10
|
+
# Bind this Proc to an object.
|
11
|
+
#
|
12
|
+
def bind(object)
|
13
|
+
block, time = self, Time.now
|
14
|
+
(class << object; self end).class_eval do
|
15
|
+
method_name = "__bind_#{time.to_i}_#{time.usec}"
|
16
|
+
define_method(method_name, &block)
|
17
|
+
method = instance_method(method_name)
|
18
|
+
remove_method(method_name)
|
19
|
+
method
|
20
|
+
end.bind(object)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# A Factory is a wrapper around a Proc that exposes it through its {Factory#new
|
26
|
+
# new} method.
|
27
|
+
#
|
28
|
+
# Wrapping a Proc in a Factory is useful to have a uniform API across classes
|
29
|
+
# and custom object-creating lambdas. For instance, if a method create_object
|
30
|
+
# takes a class as argument, like:
|
31
|
+
#
|
32
|
+
# def create_object(klass)
|
33
|
+
# obj = klass.new('foo')
|
34
|
+
# # do something with obj
|
35
|
+
# obj
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# you can pass modified class constructors:
|
39
|
+
#
|
40
|
+
# create_object(Factory.new {|arg| Array.new(4) { arg } })
|
41
|
+
#
|
42
|
+
# and have the method behave as if the passed argument were a normal class.
|
43
|
+
#
|
44
|
+
class Factory
|
45
|
+
#
|
46
|
+
# A Factory can specify a <b>component</b>, which is the class used to
|
47
|
+
# instantiate the objects created by this Factory.
|
48
|
+
#
|
49
|
+
# When non-nil, it should satisfy <tt>component == new(*args).class</tt>.
|
50
|
+
#
|
51
|
+
# @return the component of this Factory
|
52
|
+
#
|
53
|
+
attr_reader :component
|
54
|
+
|
55
|
+
#
|
56
|
+
# Create a factory object.
|
57
|
+
#
|
58
|
+
# @param component[Class] the factory component
|
59
|
+
# @param &blk the wrapped Proc
|
60
|
+
#
|
61
|
+
def initialize(component = nil, &blk)
|
62
|
+
@blk = blk
|
63
|
+
@component = component
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Call the wrapped Proc
|
68
|
+
#
|
69
|
+
def new(*args)
|
70
|
+
@blk[*args]
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Rebind this Factory.
|
75
|
+
#
|
76
|
+
# Binding a Factory to an object causes the wrapped Proc to be executed in
|
77
|
+
# the given object's scope.
|
78
|
+
#
|
79
|
+
# @param object the object to bind this Factory to
|
80
|
+
#
|
81
|
+
def __bind__(object)
|
82
|
+
Factory.new(@component, &@blk.bind(object))
|
83
|
+
end
|
84
|
+
end
|