qml 0.0.5 → 0.0.6
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.
- 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
|