qml 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +57 -4
- data/changes.md +8 -0
- data/examples/fizzbuzz/fizzbuzz.rb +1 -1
- data/examples/imageprovider/imageprovider.rb +1 -1
- data/examples/todo_array/todo_array.rb +1 -1
- data/examples/todo_sequel/todo_sequel.rb +1 -1
- data/examples/twitter/twitter.rb +1 -1
- data/ext/qml/accessclass.cpp +5 -9
- data/ext/qml/conversionerror.h +14 -0
- data/ext/qml/ext_metaobject.cpp +30 -41
- data/ext/qml/init.cpp +5 -7
- data/ext/qml/rubyclass.cpp +6 -18
- data/ext/qml/rubyclass.h +22 -26
- data/ext/qml/rubyvalue.cpp +155 -221
- data/ext/qml/rubyvalue.h +30 -63
- data/ext/qml/signalforwarder.cpp +1 -3
- data/ext/qml/util.cpp +2 -25
- data/ext/qml/util.h +1 -21
- data/ext/qml/weakvaluereference.cpp +4 -5
- data/lib/qml/access.rb +9 -17
- data/lib/qml/application.rb +18 -21
- data/lib/qml/context.rb +1 -1
- data/lib/qml/dispatcher.rb +0 -1
- data/lib/qml/error_converter.rb +1 -0
- data/lib/qml/meta_object.rb +3 -3
- data/lib/qml/qt_object_base.rb +130 -5
- data/lib/qml/reactive/object.rb +34 -25
- data/lib/qml/reactive/signal.rb +6 -10
- data/lib/qml/reactive/unbound_property.rb +1 -1
- data/lib/qml/reactive/unbound_signal.rb +2 -2
- data/lib/qml/version.rb +1 -1
- data/spec/qml/reactive/object_spec.rb +14 -38
- data/spec/qml/reactive/signal_spec.rb +1 -1
- data/spec/qml/reactive/unbound_property_spec.rb +40 -0
- data/spec/qml/reactive/unbound_signal_spec.rb +70 -0
- data/spec/{shared_examples → shared}/qml/data/list_model.rb +0 -0
- data/spec/shared/qml/reactive/object.rb +39 -0
- data/spec/spec_helper.rb +1 -1
- metadata +12 -6
- data/lib/qml/class_builder.rb +0 -129
data/lib/qml/reactive/object.rb
CHANGED
@@ -12,31 +12,30 @@ module QML
|
|
12
12
|
|
13
13
|
# When {Object} is included by a class, the class extends {ClassMethods} to add the common class methods.
|
14
14
|
def self.included(derived)
|
15
|
-
fail Error, "
|
15
|
+
fail Error, "#{Object} must be included in a class" unless derived.is_a? ::Class
|
16
16
|
derived.extend(ClassMethods)
|
17
17
|
end
|
18
18
|
|
19
19
|
def initialize(*args, &block)
|
20
|
-
|
20
|
+
# prepare properties
|
21
21
|
properties = self.class.instance_property_hash
|
22
22
|
.values
|
23
23
|
.map { |property| self.class.instance_property(property.original) }
|
24
24
|
.uniq
|
25
|
-
|
25
|
+
properties.each do |property|
|
26
|
+
instance_variable_set("@_property_#{property.name}", property.bind(self))
|
27
|
+
end
|
28
|
+
|
29
|
+
# prepare signals
|
26
30
|
signals = self.class.instance_signal_hash
|
27
31
|
.values
|
28
32
|
.map { |signal| self.class.instance_signal(signal.original) }
|
29
|
-
.uniq
|
30
|
-
|
31
|
-
properties.each do |property|
|
32
|
-
p = property.bind(self)
|
33
|
-
instance_variable_set(:"@_property_#{property.name}", p)
|
34
|
-
instance_variable_set(:"@_signal_#{property.notifier_signal.name}", p.changed)
|
35
|
-
end
|
33
|
+
.uniq
|
36
34
|
signals.each do |signal|
|
37
|
-
instance_variable_set(
|
35
|
+
instance_variable_set("@_signal_#{signal.name}", signal.bind(self))
|
38
36
|
end
|
39
37
|
|
38
|
+
# prepare signal connections
|
40
39
|
self.class.send(:initial_connections_hash).each do |name, blocks|
|
41
40
|
blocks.each do |block|
|
42
41
|
signal(name).connect do |*args|
|
@@ -44,6 +43,8 @@ module QML
|
|
44
43
|
end
|
45
44
|
end
|
46
45
|
end
|
46
|
+
|
47
|
+
super
|
47
48
|
end
|
48
49
|
|
49
50
|
# @!method signal(name)
|
@@ -126,7 +127,7 @@ module QML
|
|
126
127
|
# @param name [#to_sym] The signal name
|
127
128
|
# @param params [Array<#to_sym>, nil] The signal parameter names
|
128
129
|
# @param opts [Hash]
|
129
|
-
# @option opts [Proc] :
|
130
|
+
# @option opts [Proc] :signal (nil)
|
130
131
|
# @return [Symbol] The signal name
|
131
132
|
# @example
|
132
133
|
# class Button
|
@@ -151,33 +152,37 @@ module QML
|
|
151
152
|
def signal(name, params, opts = {})
|
152
153
|
name.to_sym.tap do |name|
|
153
154
|
params = params.map(&:to_sym)
|
154
|
-
add_signal(UnboundSignal.new(name, params, false, self, opts[:
|
155
|
+
add_signal(UnboundSignal.new(name, params, false, self, opts[:signal]))
|
155
156
|
end
|
156
157
|
end
|
157
158
|
|
158
159
|
# Defines a variadic signal.
|
159
160
|
# Variadic signals do not restrict the number of arguments.
|
160
161
|
# @param opts [Hash]
|
161
|
-
# @option opts [Proc] :
|
162
|
+
# @option opts [Proc] :signal (nil)
|
162
163
|
# @see #signal
|
163
164
|
def variadic_signal(name, opts = {})
|
164
165
|
name.to_sym.tap do |name|
|
165
|
-
add_signal(UnboundSignal.new(name, nil, true, self, opts[:
|
166
|
+
add_signal(UnboundSignal.new(name, nil, true, self, opts[:signal]))
|
166
167
|
end
|
167
168
|
end
|
168
169
|
|
169
170
|
# Aliases a signal.
|
171
|
+
# @param name [#to_sym]
|
172
|
+
# @param original_name [#to_sym]
|
170
173
|
# @return [Symbol] The new name
|
171
174
|
def alias_signal(name, original_name)
|
172
|
-
|
173
|
-
|
175
|
+
name.to_sym.tap do |name|
|
176
|
+
original_name = original_name.to_sym
|
177
|
+
add_signal(instance_signal(original_name).alias(name))
|
178
|
+
end
|
174
179
|
end
|
175
180
|
|
176
181
|
# Defines a property for the class.
|
177
182
|
# @param name [#to_sym] The name of the property
|
178
183
|
# @param init_value The initial value (optional)
|
179
184
|
# @param opts [Hash]
|
180
|
-
# @option opts [Proc] :
|
185
|
+
# @option opts [Proc] :property (nil)
|
181
186
|
# @yield The initial property binding (optional)
|
182
187
|
# @return [Symbol] The name
|
183
188
|
# @example
|
@@ -197,16 +202,22 @@ module QML
|
|
197
202
|
# end
|
198
203
|
# Bar.new.name #=> 'piyopiyo'
|
199
204
|
def property(name, init_value = nil, opts = {}, &init_binding)
|
200
|
-
name
|
201
|
-
|
202
|
-
|
205
|
+
name.to_sym.tap do |name|
|
206
|
+
add_property(UnboundProperty.new(name, init_value, init_binding, self, opts[:property]))
|
207
|
+
signal("#{name}_changed", [:new_value], signal: -> { property(name).changed })
|
208
|
+
end
|
203
209
|
end
|
204
210
|
|
205
211
|
# Aliases a property.
|
212
|
+
# @param name [#to_sym]
|
213
|
+
# @param original_name [#to_sym]
|
206
214
|
# @return [Symbol] The new name
|
207
215
|
def alias_property(name, original_name)
|
208
|
-
|
209
|
-
|
216
|
+
name.to_sym.tap do |name|
|
217
|
+
original_name = original_name.to_sym
|
218
|
+
add_property(instance_property(original_name).alias(name))
|
219
|
+
alias_signal("#{name}_changed", "#{original_name}_changed")
|
220
|
+
end
|
210
221
|
end
|
211
222
|
|
212
223
|
# Adds a signal handler.
|
@@ -261,8 +272,6 @@ module QML
|
|
261
272
|
@_property_#{property.original}.value = new_value
|
262
273
|
end
|
263
274
|
EOS
|
264
|
-
|
265
|
-
add_signal(property.notifier_signal)
|
266
275
|
end
|
267
276
|
|
268
277
|
def initial_connections_hash(include_super = true)
|
data/lib/qml/reactive/signal.rb
CHANGED
@@ -18,19 +18,15 @@ module QML
|
|
18
18
|
attr_reader :arity
|
19
19
|
|
20
20
|
# Initializes the Signal.
|
21
|
-
#
|
22
|
-
|
23
|
-
# @param [Hash] opts
|
24
|
-
# @option opts [Boolean] (false) :variadic
|
25
|
-
def initialize(params, opts = {})
|
26
|
-
opts = {variadic: false}.merge opts
|
21
|
+
# @param [Array<#to_sym>, nil] params the parameter names (the signal will be variadic if nil).
|
22
|
+
def initialize(params)
|
27
23
|
@listeners = []
|
28
|
-
if
|
29
|
-
@params = nil
|
30
|
-
@arity = -1
|
31
|
-
else
|
24
|
+
if params
|
32
25
|
@params = params.map(&:to_sym)
|
33
26
|
@arity = params.size
|
27
|
+
else
|
28
|
+
@params = nil
|
29
|
+
@arity = -1
|
34
30
|
end
|
35
31
|
end
|
36
32
|
|
@@ -31,7 +31,7 @@ module QML
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def bind(obj)
|
34
|
-
return
|
34
|
+
return obj.instance_exec(&@factory) if @factory
|
35
35
|
Property.new.tap do |p|
|
36
36
|
p.value = @initial_value if @initial_value
|
37
37
|
p.bind { obj.instance_eval &@initial_binding } if @initial_binding
|
data/lib/qml/version.rb
CHANGED
@@ -2,36 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe QML::Reactive::Object do
|
4
4
|
|
5
|
-
|
6
|
-
include QML::Reactive::Object
|
7
|
-
signal :pressed, [:pos]
|
8
|
-
variadic_signal :message
|
9
|
-
property :name, 'button'
|
10
|
-
property :id, 0
|
11
|
-
property :name_double do
|
12
|
-
name + name
|
13
|
-
end
|
14
|
-
on :pressed do |pos|
|
15
|
-
self.name = pos
|
16
|
-
end
|
17
|
-
on_changed :id do
|
18
|
-
self.name = "ID: #{id}"
|
19
|
-
end
|
20
|
-
on_changed :id do
|
21
|
-
end
|
22
|
-
alias_property :title, :name
|
23
|
-
alias_signal :clicked, :pressed
|
24
|
-
end
|
25
|
-
|
26
|
-
class ToggleButton < Button
|
27
|
-
signal :pressed, [:x, :y]
|
28
|
-
signal :toggled, [:on]
|
29
|
-
property(:name) { 'toggle button' }
|
30
|
-
property(:info) { 'some info' }
|
31
|
-
end
|
32
|
-
|
33
|
-
let(:button) { Button.new }
|
34
|
-
let(:toggle_button) { ToggleButton.new }
|
5
|
+
include_context 'Reactive test objects'
|
35
6
|
|
36
7
|
describe '.property' do
|
37
8
|
|
@@ -175,19 +146,24 @@ describe QML::Reactive::Object do
|
|
175
146
|
button.id = 2
|
176
147
|
expect(button.name).to eq "ID: 2"
|
177
148
|
end
|
149
|
+
it 'calls all connections including superclass' do
|
150
|
+
toggle_button.id = 3
|
151
|
+
expect(toggle_button.name).to eq "ID: 3"
|
152
|
+
expect(toggle_button.info).to eq "ID changed"
|
153
|
+
end
|
178
154
|
end
|
179
155
|
|
180
156
|
describe '.instance_signals' do
|
181
157
|
context 'when include_super is false' do
|
182
158
|
it 'returns all signal definitions of the class' do
|
183
|
-
signals =
|
159
|
+
signals = toggle_button_class.instance_signals(false)
|
184
160
|
expect(signals).to match_array %w{pressed toggled name_changed info_changed}.map(&:to_sym)
|
185
161
|
end
|
186
162
|
end
|
187
163
|
|
188
164
|
context 'when include_super is not specified' do
|
189
165
|
it 'returns all signal definitions of the class and its superclasses' do
|
190
|
-
signals =
|
166
|
+
signals = toggle_button_class.instance_signals
|
191
167
|
expect(signals).to match_array %w{pressed message toggled name_changed id_changed info_changed name_double_changed clicked title_changed}.map(&:to_sym)
|
192
168
|
end
|
193
169
|
end
|
@@ -196,14 +172,14 @@ describe QML::Reactive::Object do
|
|
196
172
|
describe '.instance_properties' do
|
197
173
|
context 'when include_super is false' do
|
198
174
|
it 'returns all property definitions of the class' do
|
199
|
-
properties =
|
175
|
+
properties = toggle_button_class.instance_properties(false)
|
200
176
|
expect(properties).to match_array %w{name info}.map(&:to_sym)
|
201
177
|
end
|
202
178
|
end
|
203
179
|
|
204
180
|
context 'when include_super is not specified' do
|
205
181
|
it 'returns all property definitions of the class and its superclasses' do
|
206
|
-
properties =
|
182
|
+
properties = toggle_button_class.instance_properties
|
207
183
|
expect(properties).to match_array %w{name id info name_double title}.map(&:to_sym)
|
208
184
|
end
|
209
185
|
end
|
@@ -211,28 +187,28 @@ describe QML::Reactive::Object do
|
|
211
187
|
|
212
188
|
describe '.instance_signal' do
|
213
189
|
it 'returns the UnboundSignal for name' do
|
214
|
-
s =
|
190
|
+
s = toggle_button_class.instance_signal(:pressed)
|
215
191
|
expect(s).to be_a QML::Reactive::UnboundSignal
|
216
192
|
expect(s.name).to eq :pressed
|
217
193
|
expect(s.arity).to eq 2
|
218
194
|
end
|
219
195
|
context 'when signal does not exist' do
|
220
196
|
it 'fails with NameError' do
|
221
|
-
expect {
|
197
|
+
expect { toggle_button_class.instance_signal(:non_existent) }.to raise_error(NameError)
|
222
198
|
end
|
223
199
|
end
|
224
200
|
end
|
225
201
|
|
226
202
|
describe '.instance_property' do
|
227
203
|
it 'returns the UnboundProperty for name' do
|
228
|
-
p =
|
204
|
+
p = toggle_button_class.instance_property(:name)
|
229
205
|
expect(p).to be_a QML::Reactive::UnboundProperty
|
230
206
|
expect(p.name).to eq :name
|
231
207
|
expect(p.initial_binding.call).to eq 'toggle button'
|
232
208
|
end
|
233
209
|
context 'when property does not exist' do
|
234
210
|
it 'fails with NameError' do
|
235
|
-
expect {
|
211
|
+
expect { toggle_button_class.instance_property(:non_existent) }.to raise_error(NameError)
|
236
212
|
end
|
237
213
|
end
|
238
214
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe QML::Reactive::UnboundProperty do
|
4
|
+
include_context 'Reactive test objects'
|
5
|
+
let(:property) { button_class.instance_property(:name) }
|
6
|
+
let(:alias_property) { button_class.instance_property(:title) }
|
7
|
+
|
8
|
+
describe '#name' do
|
9
|
+
it 'returns the name' do
|
10
|
+
expect(property.name).to eq(:name)
|
11
|
+
expect(alias_property.name).to eq(:title)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#owner' do
|
16
|
+
it 'returns the owner class' do
|
17
|
+
expect(property.owner).to be(button_class)
|
18
|
+
expect(alias_property.owner).to be(button_class)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#original' do
|
23
|
+
it 'returns the original name of the alias property' do
|
24
|
+
expect(alias_property.original).to eq(:name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#alias?' do
|
29
|
+
context 'for non-alias properties' do
|
30
|
+
it 'returns false' do
|
31
|
+
expect(property).not_to be_alias
|
32
|
+
end
|
33
|
+
end
|
34
|
+
context 'for alias properties' do
|
35
|
+
it 'returns true' do
|
36
|
+
expect(alias_property).to be_alias
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe QML::Reactive::UnboundSignal do
|
4
|
+
include_context 'Reactive test objects'
|
5
|
+
let(:signal) { button_class.instance_signal(:pressed) }
|
6
|
+
let(:alias_signal) { button_class.instance_signal(:clicked) }
|
7
|
+
let(:variadic_signal) { button_class.instance_signal(:message) }
|
8
|
+
|
9
|
+
describe '#name' do
|
10
|
+
it 'returns the name' do
|
11
|
+
expect(signal.name).to eq(:pressed)
|
12
|
+
expect(alias_signal.name).to eq(:clicked)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#owner' do
|
17
|
+
it 'returns the owner class' do
|
18
|
+
expect(signal.owner).to be(button_class)
|
19
|
+
expect(alias_signal.owner).to be(button_class)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#original' do
|
24
|
+
it 'returns the original name of the alias signal' do
|
25
|
+
expect(alias_signal.original).to eq(:pressed)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#variadic?' do
|
30
|
+
context 'for non-variadic signals' do
|
31
|
+
it 'returns false' do
|
32
|
+
expect(signal).not_to be_variadic
|
33
|
+
end
|
34
|
+
end
|
35
|
+
context 'for variadic signals' do
|
36
|
+
it 'returns true' do
|
37
|
+
expect(variadic_signal).to be_variadic
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#alias?' do
|
43
|
+
context 'for non-alias signals' do
|
44
|
+
it 'returns false' do
|
45
|
+
expect(signal).not_to be_alias
|
46
|
+
end
|
47
|
+
end
|
48
|
+
context 'for alias signals' do
|
49
|
+
it 'returns true' do
|
50
|
+
expect(alias_signal).to be_alias
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#arity' do
|
56
|
+
it 'returns the number of arguments' do
|
57
|
+
expect(signal.arity).to eq(1)
|
58
|
+
expect(variadic_signal.arity).to eq(-1)
|
59
|
+
expect(alias_signal.arity).to eq(1)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#parameters' do
|
64
|
+
it 'returns parameter names' do
|
65
|
+
expect(signal.parameters).to eq([[:req, :pos]])
|
66
|
+
expect(alias_signal.parameters).to eq([[:req, :pos]])
|
67
|
+
expect(variadic_signal.parameters).to eq([[:rest, :args]])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
File without changes
|
@@ -0,0 +1,39 @@
|
|
1
|
+
shared_context 'Reactive test objects' do
|
2
|
+
let(:button_class) do
|
3
|
+
Class.new do
|
4
|
+
include QML::Reactive::Object
|
5
|
+
signal :pressed, [:pos]
|
6
|
+
variadic_signal :message
|
7
|
+
property :name, 'button'
|
8
|
+
property :id, 0
|
9
|
+
property :name_double do
|
10
|
+
name + name
|
11
|
+
end
|
12
|
+
on :pressed do |pos|
|
13
|
+
self.name = pos
|
14
|
+
end
|
15
|
+
on_changed :id do
|
16
|
+
self.name = "ID: #{id}"
|
17
|
+
end
|
18
|
+
on_changed :id do
|
19
|
+
end
|
20
|
+
alias_property :title, :name
|
21
|
+
alias_signal :clicked, :pressed
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:toggle_button_class) do
|
26
|
+
Class.new(button_class) do
|
27
|
+
signal :pressed, [:x, :y]
|
28
|
+
signal :toggled, [:on]
|
29
|
+
property(:name) { 'toggle button' }
|
30
|
+
property(:info) { 'some info' }
|
31
|
+
on_changed :id do
|
32
|
+
self.info = 'ID changed'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:button) { button_class.new }
|
38
|
+
let(:toggle_button) { toggle_button_class.new }
|
39
|
+
end
|