qml 0.0.5 → 0.0.6

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