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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88ec8d13931b6285ad6961a33b0fb5dd0e30582b
|
4
|
+
data.tar.gz: da96d985867d7ef034edcb60174cb4f64410783f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07886e4fd25d9f242bf9fd8e8eca6e4ed8a3bb7886fdac811b16d9ca0b699cc8269899fe2299e4fcb6d5ad51456e7749831cde2437f70cd4453bc971ee0cd488
|
7
|
+
data.tar.gz: bde9ec75fa9b85c9e54d319f686a3d98a54ae6fd23af31f94e0bda49192903f818b04b0c47df34cc4b52621332dc4c54ce19a343563fdcdcec1e12a5a1b366e9
|
data/README.md
CHANGED
@@ -46,9 +46,9 @@ Both libffi and Qt5 are keg-only in Homebrew, so you must specify their paths ex
|
|
46
46
|
If you use [official Qt installation](http://qt-project.org/downloads), for example:
|
47
47
|
|
48
48
|
$ brew install pkg-config libffi
|
49
|
-
$ gem install qml -- --with-libffi-dir=$(brew --prefix libffi) --with-qt-dir
|
49
|
+
$ gem install qml -- --with-libffi-dir=$(brew --prefix libffi) --with-qt-dir=$HOME/Qt/5.3/clang_64
|
50
50
|
|
51
|
-
The Qt installation path (
|
51
|
+
The Qt installation path (`$HOME/Qt/5.3/clang_64` in this example) depends on your Qt installation configuration and Qt version.
|
52
52
|
|
53
53
|
### General (OSX and Linux)
|
54
54
|
|
@@ -87,7 +87,7 @@ The following code loads a QML file and shows an application window titled "Hell
|
|
87
87
|
```ruby
|
88
88
|
require 'qml'
|
89
89
|
|
90
|
-
QML.
|
90
|
+
QML.run do |app|
|
91
91
|
app.load_path Pathname(__FILE__) + '../main.qml'
|
92
92
|
end
|
93
93
|
```
|
@@ -215,7 +215,7 @@ class Foo
|
|
215
215
|
end
|
216
216
|
end
|
217
217
|
|
218
|
-
QML.
|
218
|
+
QML.run do |app|
|
219
219
|
app.context[:foo] = Foo.new
|
220
220
|
app.load_path Pathname(__FILE__) + '../main.qml'
|
221
221
|
end
|
@@ -434,6 +434,59 @@ obj = plugin.create_object
|
|
434
434
|
obj.prefer_managed false
|
435
435
|
```
|
436
436
|
|
437
|
+
### Use with EventMachine
|
438
|
+
|
439
|
+
You can use [EventMachine](https://github.com/eventmachine/eventmachine) with ruby-qml.
|
440
|
+
It is more powerful than the default ruby-qml event loop.
|
441
|
+
|
442
|
+
Instead of using `QML.run`, start an EventMachine event loop by `EM.run` and
|
443
|
+
process QML events periodically by `QML::Application#process_events`.
|
444
|
+
|
445
|
+
```ruby
|
446
|
+
require 'qml'
|
447
|
+
require 'eventmachine'
|
448
|
+
|
449
|
+
EM.run do
|
450
|
+
QML.init
|
451
|
+
EM.add_periodic_timer(0.01) { QML.application.process_events }
|
452
|
+
QML.application.load_path(Pathname.pwd + 'main.qml')
|
453
|
+
end
|
454
|
+
```
|
455
|
+
|
456
|
+
You can also use [em-synchrony](https://github.com/igrigorik/em-synchrony) to
|
457
|
+
write callback-free asynchronous operation for ruby-qml.
|
458
|
+
|
459
|
+
```
|
460
|
+
require 'qml'
|
461
|
+
require 'eventmachine'
|
462
|
+
require 'em-synchrony'
|
463
|
+
require 'em-http-request'
|
464
|
+
|
465
|
+
class Controller
|
466
|
+
include QML::Access
|
467
|
+
property :result, ''
|
468
|
+
|
469
|
+
def get
|
470
|
+
EM.synchrony do
|
471
|
+
content = EM::Synchrony.sync EM::HttpRequest.new('http://www.example.com/').get
|
472
|
+
self.result = content.response
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
def quit
|
477
|
+
EM.stop
|
478
|
+
end
|
479
|
+
|
480
|
+
register_to_qml under: 'Example', version: '0.1'
|
481
|
+
end
|
482
|
+
|
483
|
+
EM.run do
|
484
|
+
QML.init
|
485
|
+
EM.add_periodic_timer(0.01) { QML.application.process_events }
|
486
|
+
QML.application.load_path(Pathname.pwd + 'main.qml')
|
487
|
+
end
|
488
|
+
```
|
489
|
+
|
437
490
|
## Contributing
|
438
491
|
|
439
492
|
Contributions are welcome. When you are contributing to ruby-qml:
|
data/changes.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## 0.0.6 (2014-08-19)
|
2
|
+
|
3
|
+
* Fix problems when using Fiber with ruby-qml
|
4
|
+
|
5
|
+
* Rename block-receiving `QML.application` to `QML.run`
|
6
|
+
|
7
|
+
* `QML.application` without block is still the same name
|
8
|
+
|
1
9
|
## 0.0.5 (2014-07-31)
|
2
10
|
|
3
11
|
* Support official Qt installation on Mac
|
data/examples/twitter/twitter.rb
CHANGED
data/ext/qml/accessclass.cpp
CHANGED
@@ -8,11 +8,9 @@ namespace RubyQml {
|
|
8
8
|
AccessClass::AccessClass(RubyValue className, RubyValue methodInfos, RubyValue signalInfos, RubyValue propertyInfos)
|
9
9
|
{
|
10
10
|
setClassName(className.to<QByteArray>());
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
rb_check_array_type(propertyInfos);
|
15
|
-
});
|
11
|
+
rb_check_array_type(methodInfos);
|
12
|
+
rb_check_array_type(signalInfos);
|
13
|
+
rb_check_array_type(propertyInfos);
|
16
14
|
for (int i = 0; i < RARRAY_LEN(VALUE(methodInfos)); ++i) {
|
17
15
|
RubyValue info = RARRAY_AREF(VALUE(methodInfos), i);
|
18
16
|
auto nameSym = info.send("name");
|
@@ -46,10 +44,8 @@ QVariant AccessClass::callMethod(ForeignObject *obj, size_t id, const QVariantLi
|
|
46
44
|
std::vector<VALUE> values(args.size());
|
47
45
|
std::transform(args.begin(), args.end(), values.begin(), &RubyValue::from<QVariant>);
|
48
46
|
RubyValue retValue;
|
49
|
-
|
50
|
-
|
51
|
-
retValue = rb_funcall2(self, id, values.size(), values.data());
|
52
|
-
});
|
47
|
+
rescueNotify([&] {
|
48
|
+
retValue = rb_funcall2(self, id, values.size(), values.data());
|
53
49
|
});
|
54
50
|
ret = retValue.to<QVariant>();
|
55
51
|
});
|
data/ext/qml/ext_metaobject.cpp
CHANGED
@@ -18,13 +18,11 @@ namespace {
|
|
18
18
|
|
19
19
|
RubyValue idListToArray(const QList<ID> &xs)
|
20
20
|
{
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
return ary;
|
27
|
-
});
|
21
|
+
auto ary = rb_ary_new();
|
22
|
+
for (ID id : xs) {
|
23
|
+
rb_ary_push(ary, ID2SYM(id));
|
24
|
+
}
|
25
|
+
return ary;
|
28
26
|
}
|
29
27
|
|
30
28
|
}
|
@@ -148,9 +146,8 @@ RubyValue Ext_MetaObject::invokeMethod(RubyValue object, RubyValue methodName, R
|
|
148
146
|
|
149
147
|
auto methodIndexes = findMethods(methodName);
|
150
148
|
|
151
|
-
|
152
|
-
|
153
|
-
});
|
149
|
+
args = rb_check_array_type(args);
|
150
|
+
|
154
151
|
auto obj = wrapperRubyClass<Ext_Pointer>().unwrap(object)->fetchQObject();
|
155
152
|
for (int i : methodIndexes) {
|
156
153
|
MethodInvoker invoker(args, mMetaObject->method(i));
|
@@ -158,17 +155,17 @@ RubyValue Ext_MetaObject::invokeMethod(RubyValue object, RubyValue methodName, R
|
|
158
155
|
return invoker.invoke(obj);
|
159
156
|
}
|
160
157
|
}
|
161
|
-
protect([&] {
|
162
|
-
auto to_class = rb_funcall(ID2SYM(rb_intern("class")), rb_intern("to_proc"), 0);
|
163
|
-
auto classes = rb_funcall_with_block(args, rb_intern("map"), 0, nullptr, to_class);
|
164
|
-
auto classes_str = rb_funcall(classes, rb_intern("to_s"), 0);
|
165
158
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
159
|
+
auto to_class = rb_funcall(ID2SYM(rb_intern("class")), rb_intern("to_proc"), 0);
|
160
|
+
auto classes = rb_funcall_with_block(args, rb_intern("map"), 0, nullptr, to_class);
|
161
|
+
auto classes_str = rb_funcall(classes, rb_intern("to_s"), 0);
|
162
|
+
|
163
|
+
rb_raise(rb_path2class("QML::MethodError"),
|
164
|
+
"method mismatch (%s with params %s in %s)",
|
165
|
+
mMetaObject->method(methodIndexes.first()).name().data(),
|
166
|
+
StringValueCStr(classes_str),
|
167
|
+
mMetaObject->className());
|
168
|
+
|
172
169
|
return Qnil;
|
173
170
|
}
|
174
171
|
|
@@ -190,11 +187,9 @@ RubyValue Ext_MetaObject::connectSignal(RubyValue object, RubyValue signalName,
|
|
190
187
|
new SignalForwarder(obj, method, proc);
|
191
188
|
return Qnil;
|
192
189
|
}
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
rb_id2name(id), mMetaObject->className());
|
197
|
-
});
|
190
|
+
rb_raise(rb_path2class("QML::MethodError"),
|
191
|
+
"signal not found (%s in %s)",
|
192
|
+
rb_id2name(id), mMetaObject->className());
|
198
193
|
return Qnil;
|
199
194
|
}
|
200
195
|
|
@@ -223,11 +218,9 @@ RubyValue Ext_MetaObject::setProperty(RubyValue object, RubyValue name, RubyValu
|
|
223
218
|
|
224
219
|
auto metaProperty = mMetaObject->property(findProperty(name));
|
225
220
|
if (!newValue.isConvertibleTo(metaProperty.userType())) {
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
rb_obj_classname(newValue), metaProperty.typeName());
|
230
|
-
});
|
221
|
+
rb_raise(rb_path2class("QML::PropertyError"),
|
222
|
+
"type mismatch (%s for %s)",
|
223
|
+
rb_obj_classname(newValue), metaProperty.typeName());
|
231
224
|
}
|
232
225
|
|
233
226
|
auto qobj = wrapperRubyClass<Ext_Pointer>().unwrap(object)->fetchQObject();
|
@@ -264,12 +257,10 @@ QList<int> Ext_MetaObject::findMethods(RubyValue name) const
|
|
264
257
|
auto id = name.toID();
|
265
258
|
auto methodIndexes = mMethodHash.values(id);
|
266
259
|
if (methodIndexes.size() == 0) {
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
mMetaObject->className());
|
272
|
-
});
|
260
|
+
rb_raise(rb_path2class("QML::MethodError"),
|
261
|
+
"method not found (%s in %s)",
|
262
|
+
rb_id2name(id),
|
263
|
+
mMetaObject->className());
|
273
264
|
}
|
274
265
|
return methodIndexes;
|
275
266
|
}
|
@@ -278,11 +269,9 @@ int Ext_MetaObject::findProperty(RubyValue name) const
|
|
278
269
|
{
|
279
270
|
auto id = name.toID();
|
280
271
|
if (!mPropertyHash.contains(id)) {
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
rb_id2name(id), mMetaObject->className());
|
285
|
-
});
|
272
|
+
rb_raise(rb_path2class("QML::PropertyError"),
|
273
|
+
"property not found (%s in %s)",
|
274
|
+
rb_id2name(id), mMetaObject->className());
|
286
275
|
}
|
287
276
|
return mPropertyHash[id];
|
288
277
|
}
|
data/ext/qml/init.cpp
CHANGED
@@ -48,7 +48,7 @@ void cleanup()
|
|
48
48
|
|
49
49
|
void setupEndProc()
|
50
50
|
{
|
51
|
-
rb_set_end_proc([](VALUE) {
|
51
|
+
rb_set_end_proc([](VALUE) { cleanup(); }, Qnil);
|
52
52
|
}
|
53
53
|
|
54
54
|
}
|
@@ -60,10 +60,8 @@ void Init_qml()
|
|
60
60
|
rb_require("qml/errors");
|
61
61
|
rb_require("qml/error_converter");
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
setupEndProc();
|
68
|
-
});
|
63
|
+
defineMetaTypes();
|
64
|
+
defineClasses();
|
65
|
+
setupGlobalGCMarking();
|
66
|
+
setupEndProc();
|
69
67
|
}
|
data/ext/qml/rubyclass.cpp
CHANGED
@@ -15,16 +15,12 @@ RubyModule::RubyModule(RubyValue moduleValue) :
|
|
15
15
|
|
16
16
|
RubyModule::RubyModule(const char *name)
|
17
17
|
{
|
18
|
-
|
19
|
-
mValue = rb_define_module(name);
|
20
|
-
});
|
18
|
+
mValue = rb_define_module(name);
|
21
19
|
}
|
22
20
|
|
23
21
|
RubyModule::RubyModule(const RubyModule &under, const char *name)
|
24
22
|
{
|
25
|
-
|
26
|
-
mValue = rb_define_module_under(under.toValue(), name);
|
27
|
-
});
|
23
|
+
mValue = rb_define_module_under(under.toValue(), name);
|
28
24
|
}
|
29
25
|
|
30
26
|
RubyModule &RubyModule::operator=(const RubyModule &other)
|
@@ -36,23 +32,17 @@ RubyModule &RubyModule::operator=(const RubyModule &other)
|
|
36
32
|
|
37
33
|
RubyModule RubyModule::fromPath(const char *path)
|
38
34
|
{
|
39
|
-
|
40
|
-
protect([&] {
|
41
|
-
ret = rb_path2class(path);
|
42
|
-
});
|
43
|
-
return ret;
|
35
|
+
return rb_path2class(path);
|
44
36
|
}
|
45
37
|
|
46
38
|
void RubyModule::aliasMethod(const char *newName, const char *originalName)
|
47
39
|
{
|
48
|
-
|
49
|
-
rb_alias(mValue, rb_intern(newName), rb_intern(originalName));
|
50
|
-
});
|
40
|
+
rb_alias(mValue, rb_intern(newName), rb_intern(originalName));
|
51
41
|
}
|
52
42
|
|
53
43
|
void RubyModule::checkType()
|
54
44
|
{
|
55
|
-
if (!
|
45
|
+
if (!mValue.isKindOf(rb_cModule)) {
|
56
46
|
throw std::logic_error("expected Module value");
|
57
47
|
}
|
58
48
|
}
|
@@ -67,9 +57,7 @@ namespace {
|
|
67
57
|
|
68
58
|
RubyValue defineClass(const RubyModule &under, const char *name)
|
69
59
|
{
|
70
|
-
return
|
71
|
-
return rb_define_class_under(under.toValue(), name, rb_cObject);
|
72
|
-
});
|
60
|
+
return rb_define_class_under(under.toValue(), name, rb_cObject);
|
73
61
|
}
|
74
62
|
|
75
63
|
}
|
data/ext/qml/rubyclass.h
CHANGED
@@ -30,26 +30,24 @@ public:
|
|
30
30
|
auto func = (VALUE (*)(...))wrapper::apply;
|
31
31
|
auto argc = wrapper::argc - 1;
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
rb_define_method(mValue , name, func, argc);
|
39
|
-
break;
|
40
|
-
case MethodAccess::Protected:
|
41
|
-
rb_define_protected_method(mValue, name, func, argc);
|
42
|
-
break;
|
43
|
-
case MethodAccess::Private:
|
44
|
-
rb_define_private_method(mValue, name, func, argc);
|
45
|
-
break;
|
46
|
-
}
|
33
|
+
switch (type) {
|
34
|
+
case MethodType::InstanceMethod:
|
35
|
+
switch (access) {
|
36
|
+
case MethodAccess::Public:
|
37
|
+
rb_define_method(mValue , name, func, argc);
|
47
38
|
break;
|
48
|
-
case
|
49
|
-
|
39
|
+
case MethodAccess::Protected:
|
40
|
+
rb_define_protected_method(mValue, name, func, argc);
|
41
|
+
break;
|
42
|
+
case MethodAccess::Private:
|
43
|
+
rb_define_private_method(mValue, name, func, argc);
|
50
44
|
break;
|
51
45
|
}
|
52
|
-
|
46
|
+
break;
|
47
|
+
case MethodType::ModuleFunction:
|
48
|
+
rb_define_module_function(mValue, name, func, argc);
|
49
|
+
break;
|
50
|
+
}
|
53
51
|
}
|
54
52
|
|
55
53
|
template <typename T>
|
@@ -92,7 +90,7 @@ private:
|
|
92
90
|
RubyValue ret;
|
93
91
|
// use tuple to avoid gcc 4.8's bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55914)
|
94
92
|
auto tuple = std::make_tuple(args...);
|
95
|
-
|
93
|
+
convertCppErrors([&] {
|
96
94
|
ret = applyWithTuple(function, tuple);
|
97
95
|
});
|
98
96
|
return ret;
|
@@ -133,19 +131,17 @@ public:
|
|
133
131
|
}
|
134
132
|
mInstance.reset(new WrapperRubyClass(*this));
|
135
133
|
|
136
|
-
|
137
|
-
rb_define_alloc_func(toValue(), &alloc);
|
138
|
-
});
|
134
|
+
rb_define_alloc_func(toValue(), &alloc);
|
139
135
|
}
|
140
136
|
|
141
137
|
T *unwrap(RubyValue value)
|
142
138
|
{
|
143
139
|
auto klass = this->toValue();
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
140
|
+
|
141
|
+
if (!RTEST(rb_obj_is_kind_of(value, klass))) {
|
142
|
+
rb_raise(rb_eTypeError, "expected %s, got %s", rb_class2name(klass), rb_obj_classname(value));
|
143
|
+
}
|
144
|
+
|
149
145
|
T *ptr;
|
150
146
|
Data_Get_Struct(VALUE(value), T, ptr);
|
151
147
|
return ptr;
|