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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +57 -4
  3. data/changes.md +8 -0
  4. data/examples/fizzbuzz/fizzbuzz.rb +1 -1
  5. data/examples/imageprovider/imageprovider.rb +1 -1
  6. data/examples/todo_array/todo_array.rb +1 -1
  7. data/examples/todo_sequel/todo_sequel.rb +1 -1
  8. data/examples/twitter/twitter.rb +1 -1
  9. data/ext/qml/accessclass.cpp +5 -9
  10. data/ext/qml/conversionerror.h +14 -0
  11. data/ext/qml/ext_metaobject.cpp +30 -41
  12. data/ext/qml/init.cpp +5 -7
  13. data/ext/qml/rubyclass.cpp +6 -18
  14. data/ext/qml/rubyclass.h +22 -26
  15. data/ext/qml/rubyvalue.cpp +155 -221
  16. data/ext/qml/rubyvalue.h +30 -63
  17. data/ext/qml/signalforwarder.cpp +1 -3
  18. data/ext/qml/util.cpp +2 -25
  19. data/ext/qml/util.h +1 -21
  20. data/ext/qml/weakvaluereference.cpp +4 -5
  21. data/lib/qml/access.rb +9 -17
  22. data/lib/qml/application.rb +18 -21
  23. data/lib/qml/context.rb +1 -1
  24. data/lib/qml/dispatcher.rb +0 -1
  25. data/lib/qml/error_converter.rb +1 -0
  26. data/lib/qml/meta_object.rb +3 -3
  27. data/lib/qml/qt_object_base.rb +130 -5
  28. data/lib/qml/reactive/object.rb +34 -25
  29. data/lib/qml/reactive/signal.rb +6 -10
  30. data/lib/qml/reactive/unbound_property.rb +1 -1
  31. data/lib/qml/reactive/unbound_signal.rb +2 -2
  32. data/lib/qml/version.rb +1 -1
  33. data/spec/qml/reactive/object_spec.rb +14 -38
  34. data/spec/qml/reactive/signal_spec.rb +1 -1
  35. data/spec/qml/reactive/unbound_property_spec.rb +40 -0
  36. data/spec/qml/reactive/unbound_signal_spec.rb +70 -0
  37. data/spec/{shared_examples → shared}/qml/data/list_model.rb +0 -0
  38. data/spec/shared/qml/reactive/object.rb +39 -0
  39. data/spec/spec_helper.rb +1 -1
  40. metadata +12 -6
  41. data/lib/qml/class_builder.rb +0 -129
@@ -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, "SignalDef must be included in a class" unless derived.is_a? ::Class
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
- super
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
- notifier_signals = properties.map(&:notifier_signal)
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 - notifier_signals
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(:"@_signal_#{signal.name}", signal.bind(self))
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] :factory (nil)
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[:factory]))
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] :factory (nil)
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[:factory]))
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
- add_signal(instance_signal(original_name).alias(name))
173
- name
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] :factory (nil)
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 = name.to_sym
201
- add_property(UnboundProperty.new(name, init_value, init_binding, self, opts[:factory]))
202
- name
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
- add_property(instance_property(original_name).alias(name))
209
- name
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)
@@ -18,19 +18,15 @@ module QML
18
18
  attr_reader :arity
19
19
 
20
20
  # Initializes the Signal.
21
- # The signal will be variadic if an empty array is given.
22
- # @param [Array<#to_sym>, Array<()>] params The parameter names.
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 opts[:variadic]
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 @factory.call(obj, self) if @factory
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
@@ -39,9 +39,9 @@ module QML
39
39
  end
40
40
 
41
41
  def bind(obj)
42
- return @factory.call(obj, self) if @factory
42
+ return obj.instance_exec(&@factory) if @factory
43
43
  if @variadic
44
- Signal.new([], variadic: true)
44
+ Signal.new(nil)
45
45
  else
46
46
  Signal.new(@params)
47
47
  end
@@ -1,3 +1,3 @@
1
1
  module QML
2
- VERSION = '0.0.5'
2
+ VERSION = '0.0.6'
3
3
  end
@@ -2,36 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe QML::Reactive::Object do
4
4
 
5
- class Button
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 = ToggleButton.instance_signals(false)
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 = ToggleButton.instance_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 = ToggleButton.instance_properties(false)
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 = ToggleButton.instance_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 = ToggleButton.instance_signal(:pressed)
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 { ToggleButton.instance_signal(:non_existent) }.to raise_error(NameError)
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 = ToggleButton.instance_property(:name)
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 { ToggleButton.instance_property(:non_existent) }.to raise_error(NameError)
211
+ expect { toggle_button_class.instance_property(:non_existent) }.to raise_error(NameError)
236
212
  end
237
213
  end
238
214
  end
@@ -4,7 +4,7 @@ describe QML::Reactive::Signal do
4
4
 
5
5
  before do
6
6
  @signal = QML::Reactive::Signal.new([:foo, :bar])
7
- @variadic_signal = QML::Reactive::Signal.new([], variadic: true)
7
+ @variadic_signal = QML::Reactive::Signal.new(nil)
8
8
  end
9
9
 
10
10
  describe 'connection' do
@@ -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
@@ -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