qml 0.0.1 → 0.0.2
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/Gemfile +2 -0
- data/README.md +82 -19
- data/changes.md +17 -0
- data/examples/fizzbuzz/capture.png +0 -0
- data/examples/todo_array/capture.png +0 -0
- data/examples/{todo → todo_array}/main.qml +1 -1
- data/examples/{todo/todo.rb → todo_array/todo_array.rb} +4 -7
- data/examples/todo_sequel/capture.png +0 -0
- data/examples/todo_sequel/main.qml +87 -0
- data/examples/todo_sequel/todo_sequel.rb +66 -0
- data/examples/twitter/capture.png +0 -0
- data/examples/twitter/main.qml +31 -19
- data/examples/twitter/twitter.rb +46 -23
- data/ext/qml/accessclass.cpp +1 -3
- data/ext/qml/ext_anywrapper.cpp +36 -0
- data/ext/qml/ext_anywrapper.h +26 -0
- data/ext/qml/ext_metaobject.cpp +3 -1
- data/ext/qml/init.cpp +3 -3
- data/ext/qml/listmodel.cpp +29 -4
- data/ext/qml/listmodel.h +2 -0
- data/ext/qml/util.cpp +7 -0
- data/ext/qml/util.h +1 -0
- data/ext/qml/weakvaluereference.cpp +34 -9
- data/ext/qml/weakvaluereference.h +7 -5
- data/lib/qml/class_builder.rb +1 -0
- data/lib/qml/data.rb +1 -0
- data/lib/qml/data/array_model.rb +23 -2
- data/lib/qml/data/list_model.rb +26 -26
- data/lib/qml/data/query_model.rb +60 -0
- data/lib/qml/version.rb +1 -1
- data/qml.gemspec +5 -1
- data/spec/qml/data/array_model_spec.rb +43 -56
- data/spec/qml/data/list_model_spec.rb +17 -0
- data/spec/qml/data/query_model_spec.rb +62 -0
- data/spec/shared_examples/qml/data/list_model.rb +33 -0
- data/spec/spec_helper.rb +7 -0
- metadata +70 -30
- data/ext/qml/ext_gcmarker.cpp +0 -39
- data/ext/qml/ext_gcmarker.h +0 -27
- data/spec/qml/.access_spec.rb.swp +0 -0
data/examples/twitter/twitter.rb
CHANGED
@@ -2,43 +2,53 @@ $LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
|
|
2
2
|
require 'qml'
|
3
3
|
require 'twitter'
|
4
4
|
require 'yaml'
|
5
|
+
require 'celluloid'
|
5
6
|
|
6
7
|
module Examples
|
7
8
|
module Twitter
|
8
9
|
VERSION = '0.1'
|
9
10
|
|
10
|
-
class
|
11
|
-
|
11
|
+
class TweetFetcher
|
12
|
+
def initialize
|
13
|
+
config_data = YAML.load((Pathname(__FILE__) + '../config.yml').read)
|
14
|
+
config_keys = %w{consumer_key consumer_secret access_token access_token_secret}
|
15
|
+
|
16
|
+
@rest_client = ::Twitter::REST::Client.new do |config|
|
17
|
+
config_keys.each do |key|
|
18
|
+
config.public_send("#{key}=", config_data[key])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
@streaming_client = ::Twitter::Streaming::Client.new do |config|
|
22
|
+
config_keys.each do |key|
|
23
|
+
config.public_send("#{key}=", config_data[key])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def start(word)
|
29
|
+
puts "word = #{word}"
|
30
|
+
@rest_client.search(word).take(10).each do |t|
|
31
|
+
yield t
|
32
|
+
end
|
33
|
+
@streaming_client.filter(track: word) do |object|
|
34
|
+
case object
|
35
|
+
when ::Twitter::Tweet
|
36
|
+
puts object
|
37
|
+
QML.later { yield object }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
12
41
|
end
|
13
42
|
|
14
43
|
class TwitterController
|
15
44
|
include QML::Access
|
16
45
|
register_to_qml
|
17
46
|
|
18
|
-
property :model,
|
47
|
+
property :model, QML::Data::ArrayModel.new(:tweet_text, :user_name, :user_icon)
|
48
|
+
property :word
|
19
49
|
|
20
50
|
def initialize
|
21
51
|
super()
|
22
|
-
|
23
|
-
streaming_client = ::Twitter::Streaming::Client.new do |config|
|
24
|
-
data = YAML.load((Pathname(__FILE__) + '../config.yml').read)
|
25
|
-
%w{consumer_key consumer_secret access_token access_token_secret}.each do |key|
|
26
|
-
config.public_send("#{key}=", data[key])
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
Thread.new do
|
31
|
-
begin
|
32
|
-
streaming_client.user do |object|
|
33
|
-
case object
|
34
|
-
when ::Twitter::Tweet
|
35
|
-
later.add_tweet(object)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
rescue => e
|
39
|
-
puts e.message
|
40
|
-
end
|
41
|
-
end
|
42
52
|
end
|
43
53
|
|
44
54
|
def add_tweet(tweet)
|
@@ -46,6 +56,19 @@ module Examples
|
|
46
56
|
puts hash
|
47
57
|
model.unshift hash
|
48
58
|
end
|
59
|
+
|
60
|
+
def fetch_tweets
|
61
|
+
model.clear
|
62
|
+
if @thread
|
63
|
+
@thread.kill
|
64
|
+
end
|
65
|
+
word = self.word
|
66
|
+
@thread = Thread.new do
|
67
|
+
TweetFetcher.new.start(word) do |tweet|
|
68
|
+
later.add_tweet(tweet)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
49
72
|
end
|
50
73
|
end
|
51
74
|
end
|
data/ext/qml/accessclass.cpp
CHANGED
@@ -47,10 +47,8 @@ QVariant AccessClass::callMethod(ForeignObject *obj, size_t id, const QVariantLi
|
|
47
47
|
std::transform(args.begin(), args.end(), values.begin(), &RubyValue::from<QVariant>);
|
48
48
|
RubyValue retValue;
|
49
49
|
protect([&] {
|
50
|
-
|
50
|
+
rescueNotify([&] {
|
51
51
|
retValue = rb_funcall2(self, id, values.size(), values.data());
|
52
|
-
}, [&](RubyValue excObject) {
|
53
|
-
rb_funcall(rb_path2class("QML::Application"), rb_intern("notify_error"), 1, excObject);
|
54
52
|
});
|
55
53
|
});
|
56
54
|
ret = retValue.to<QVariant>();
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#include "ext_anywrapper.h"
|
2
|
+
#include "rubyclass.h"
|
3
|
+
|
4
|
+
namespace RubyQml {
|
5
|
+
namespace Ext {
|
6
|
+
|
7
|
+
AnyWrapper::AnyWrapper(RubyValue self)
|
8
|
+
{
|
9
|
+
Q_UNUSED(self);
|
10
|
+
}
|
11
|
+
|
12
|
+
RubyValue AnyWrapper::create(const QVariant &value, void (*markFunction)(const QVariant &))
|
13
|
+
{
|
14
|
+
auto klass = wrapperRubyClass<AnyWrapper>();
|
15
|
+
auto wrapper = klass.newInstance();
|
16
|
+
auto ptr = klass.unwrap(wrapper);
|
17
|
+
ptr->mValue = value;
|
18
|
+
ptr->mMarkFunction = markFunction;
|
19
|
+
return wrapper;
|
20
|
+
}
|
21
|
+
|
22
|
+
void AnyWrapper::defineClass()
|
23
|
+
{
|
24
|
+
WrapperRubyClass<AnyWrapper> klass(RubyModule::fromPath("QML"), "AnyWrapper");
|
25
|
+
Q_UNUSED(klass);
|
26
|
+
}
|
27
|
+
|
28
|
+
void AnyWrapper::gc_mark()
|
29
|
+
{
|
30
|
+
if (mMarkFunction) {
|
31
|
+
mMarkFunction(mValue);
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
} // namespace Ext
|
36
|
+
} // namespace RubyQml
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#pragma once
|
2
|
+
#include <QVariant>
|
3
|
+
|
4
|
+
namespace RubyQml {
|
5
|
+
|
6
|
+
class RubyValue;
|
7
|
+
|
8
|
+
namespace Ext {
|
9
|
+
|
10
|
+
class AnyWrapper
|
11
|
+
{
|
12
|
+
public:
|
13
|
+
AnyWrapper(RubyValue self);
|
14
|
+
static RubyValue create(const QVariant &value, void (*markFunction)(const QVariant &) = nullptr);
|
15
|
+
static void defineClass();
|
16
|
+
|
17
|
+
QVariant value() const { return mValue; }
|
18
|
+
void gc_mark();
|
19
|
+
|
20
|
+
private:
|
21
|
+
QVariant mValue;
|
22
|
+
void (*mMarkFunction)(const QVariant &);
|
23
|
+
};
|
24
|
+
|
25
|
+
} // namespace Ext
|
26
|
+
} // namespace RubyQml
|
data/ext/qml/ext_metaobject.cpp
CHANGED
@@ -106,7 +106,9 @@ public:
|
|
106
106
|
bool voidReturning = (returnType == QMetaType::Void);
|
107
107
|
QVariant returnValue;
|
108
108
|
if (!voidReturning) {
|
109
|
-
|
109
|
+
auto data = QMetaType::create(returnType);
|
110
|
+
returnValue = QVariant(returnType, data);
|
111
|
+
QMetaType::destroy(returnType, data);
|
110
112
|
}
|
111
113
|
|
112
114
|
bool result;
|
data/ext/qml/init.cpp
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#include "ext_metaobject.h"
|
2
2
|
#include "ext_pointer.h"
|
3
3
|
#include "ext_pluginloader.h"
|
4
|
-
#include "
|
4
|
+
#include "ext_anywrapper.h"
|
5
5
|
#include "ext_accesssupport.h"
|
6
6
|
#include "ext_testutil.h"
|
7
7
|
#include "ext_kernel.h"
|
@@ -26,7 +26,7 @@ void defineClasses()
|
|
26
26
|
Ext::MetaObject::defineClass();
|
27
27
|
Ext::Pointer::defineClass();
|
28
28
|
Ext::PluginLoader::defineClass();
|
29
|
-
Ext::
|
29
|
+
Ext::AnyWrapper::defineClass();
|
30
30
|
Ext::AccessWrapperFactory::defineClass();
|
31
31
|
Ext::TestUtil::defineModule();
|
32
32
|
Ext::Kernel::defineModule();
|
@@ -35,7 +35,7 @@ void defineClasses()
|
|
35
35
|
|
36
36
|
void setupGlobalGCMarking()
|
37
37
|
{
|
38
|
-
auto marker = Ext::
|
38
|
+
auto marker = Ext::AnyWrapper::create(QVariant(), [](const QVariant &) {
|
39
39
|
ValueReference::markAllReferences();
|
40
40
|
ObjectGC::instance()->markNonOwnedObjects();
|
41
41
|
});
|
data/ext/qml/listmodel.cpp
CHANGED
@@ -9,7 +9,7 @@ ListModel::ListModel(RubyValue rubyModel, QObject *parent) :
|
|
9
9
|
QAbstractListModel(parent),
|
10
10
|
mRubyModel(rubyModel)
|
11
11
|
{
|
12
|
-
auto columns = rubyModel.send("
|
12
|
+
auto columns = rubyModel.send("columns").to<QList<QByteArray>>();
|
13
13
|
for (int i = 0; i < columns.size(); ++i) {
|
14
14
|
auto role = Qt::UserRole + i;
|
15
15
|
mColumnNames[role] = columns[i];
|
@@ -34,7 +34,17 @@ QVariant ListModel::data(const QModelIndex &index, int role) const
|
|
34
34
|
return QVariant();
|
35
35
|
}
|
36
36
|
|
37
|
-
|
37
|
+
QVariant result;
|
38
|
+
withGvl([&] {
|
39
|
+
RubyValue value;
|
40
|
+
rescueNotify([&] {
|
41
|
+
auto subscribe = RUBYQML_INTERN("[]");
|
42
|
+
auto record = rb_funcall(mRubyModel, subscribe, 1, INT2NUM(index.row()));
|
43
|
+
value = rb_funcall(record, subscribe, 1, ID2SYM(mColumnIDs[role]));
|
44
|
+
});
|
45
|
+
result = value.toVariant();
|
46
|
+
});
|
47
|
+
return result;
|
38
48
|
}
|
39
49
|
|
40
50
|
int ListModel::rowCount(const QModelIndex &parent) const
|
@@ -42,8 +52,13 @@ int ListModel::rowCount(const QModelIndex &parent) const
|
|
42
52
|
if (parent.isValid()) {
|
43
53
|
return 0;
|
44
54
|
}
|
45
|
-
|
46
|
-
|
55
|
+
int count;
|
56
|
+
withGvl([&] {
|
57
|
+
rescueNotify([&] {
|
58
|
+
count = NUM2INT(rb_funcall(mRubyModel, RUBYQML_INTERN("count"), 0));
|
59
|
+
});
|
60
|
+
});
|
61
|
+
return count;
|
47
62
|
}
|
48
63
|
|
49
64
|
QModelIndex ListModel::index(int row, int column, const QModelIndex &parent) const
|
@@ -84,6 +99,16 @@ void ListModel::endRemove()
|
|
84
99
|
endRemoveRows();
|
85
100
|
}
|
86
101
|
|
102
|
+
void ListModel::beginReset()
|
103
|
+
{
|
104
|
+
beginResetModel();
|
105
|
+
}
|
106
|
+
|
107
|
+
void ListModel::endReset()
|
108
|
+
{
|
109
|
+
endResetModel();
|
110
|
+
}
|
111
|
+
|
87
112
|
void ListModel::update(int first, int last)
|
88
113
|
{
|
89
114
|
emit dataChanged(index(first), index(last));
|
data/ext/qml/listmodel.h
CHANGED
data/ext/qml/util.cpp
CHANGED
@@ -71,6 +71,13 @@ void rescue(const std::function<void ()> &doAction, const std::function<void (Ru
|
|
71
71
|
rb_rescue((VALUE (*)(...))callback, (VALUE)&doAction, (VALUE (*)(...))rescueCallback, (VALUE)&handleException);
|
72
72
|
}
|
73
73
|
|
74
|
+
void rescueNotify(const std::function<void ()> &doAction)
|
75
|
+
{
|
76
|
+
rescue(doAction, [&](RubyValue excObject) {
|
77
|
+
rb_funcall(rb_path2class("QML::Application"), rb_intern("notify_error"), 1, excObject);
|
78
|
+
});
|
79
|
+
}
|
80
|
+
|
74
81
|
namespace {
|
75
82
|
|
76
83
|
void changeGvl(const std::function<void ()> &doAction, bool gvl)
|
data/ext/qml/util.h
CHANGED
@@ -77,6 +77,7 @@ protect(const F &doAction)
|
|
77
77
|
void unprotect(const std::function<void()> &doAction) noexcept;
|
78
78
|
|
79
79
|
void rescue(const std::function<void ()> &doAction, const std::function<void (RubyValue)> &handleException);
|
80
|
+
void rescueNotify(const std::function<void ()> &doAction);
|
80
81
|
|
81
82
|
// call function with GVL unlocked
|
82
83
|
void withoutGvl(const std::function<void()> &doAction);
|
@@ -1,27 +1,52 @@
|
|
1
1
|
#include "weakvaluereference.h"
|
2
|
+
#include "ext_anywrapper.h"
|
2
3
|
#include "rubyclass.h"
|
3
4
|
|
5
|
+
Q_DECLARE_METATYPE(std::shared_ptr<RubyQml::WeakValueReference::Data>)
|
6
|
+
|
4
7
|
namespace RubyQml {
|
5
8
|
|
9
|
+
struct WeakValueReference::Data
|
10
|
+
{
|
11
|
+
bool mHasValue;
|
12
|
+
RubyValue mValue;
|
13
|
+
|
14
|
+
static VALUE finalize(VALUE args, VALUE data) {
|
15
|
+
Q_UNUSED(args);
|
16
|
+
auto variant = wrapperRubyClass<Ext::AnyWrapper>().unwrap(data)->value();
|
17
|
+
auto sp = variant.value<std::shared_ptr<Data>>();
|
18
|
+
sp->invalidiate();
|
19
|
+
return Qnil;
|
20
|
+
}
|
21
|
+
|
22
|
+
void invalidiate()
|
23
|
+
{
|
24
|
+
mHasValue = false;
|
25
|
+
mValue = Qnil;
|
26
|
+
}
|
27
|
+
};
|
28
|
+
|
6
29
|
WeakValueReference::WeakValueReference(RubyValue value) :
|
7
|
-
|
8
|
-
mValue(value)
|
30
|
+
d(std::make_shared<Data>())
|
9
31
|
{
|
32
|
+
d->mHasValue = true;
|
33
|
+
d->mValue = value;
|
10
34
|
static auto objspace = RubyModule::fromPath("ObjectSpace");
|
11
35
|
protect([&] {
|
12
|
-
auto proc = rb_proc_new((VALUE (*)(...))&finalize,
|
36
|
+
auto proc = rb_proc_new((VALUE (*)(...))&Data::finalize, Ext::AnyWrapper::create(QVariant::fromValue(d)));
|
13
37
|
VALUE args[] = { value };
|
14
38
|
rb_funcall_with_block(objspace, RUBYQML_INTERN("define_finalizer"), 1, args, proc);
|
15
39
|
});
|
16
40
|
}
|
17
41
|
|
18
|
-
|
42
|
+
bool WeakValueReference::hasValue() const
|
43
|
+
{
|
44
|
+
return d->mHasValue;
|
45
|
+
}
|
46
|
+
|
47
|
+
RubyValue WeakValueReference::value() const
|
19
48
|
{
|
20
|
-
|
21
|
-
auto reference = (WeakValueReference *)data;
|
22
|
-
reference->mHasValue = false;
|
23
|
-
reference->mValue = Qnil;
|
24
|
-
return Qnil;
|
49
|
+
return d->mValue;
|
25
50
|
}
|
26
51
|
|
27
52
|
} // namespace RubyQml
|
@@ -1,5 +1,6 @@
|
|
1
1
|
#pragma once
|
2
2
|
#include "rubyvalue.h"
|
3
|
+
#include <memory>
|
3
4
|
|
4
5
|
namespace RubyQml {
|
5
6
|
|
@@ -8,12 +9,13 @@ class WeakValueReference
|
|
8
9
|
public:
|
9
10
|
WeakValueReference(RubyValue value);
|
10
11
|
|
11
|
-
bool hasValue() const
|
12
|
-
RubyValue value() const
|
12
|
+
bool hasValue() const;
|
13
|
+
RubyValue value() const;
|
14
|
+
|
15
|
+
struct Data;
|
16
|
+
|
13
17
|
private:
|
14
|
-
|
15
|
-
bool mHasValue;
|
16
|
-
RubyValue mValue;
|
18
|
+
std::shared_ptr<Data> d;
|
17
19
|
};
|
18
20
|
|
19
21
|
} // namespace RubyQml
|
data/lib/qml/class_builder.rb
CHANGED
data/lib/qml/data.rb
CHANGED
data/lib/qml/data/array_model.rb
CHANGED
@@ -2,8 +2,10 @@ module QML
|
|
2
2
|
module Data
|
3
3
|
# {ArrayModel} is one of ruby-qml's list models and it stores data in Array simply.
|
4
4
|
class ArrayModel < ListModel
|
5
|
-
|
6
|
-
|
5
|
+
|
6
|
+
# @param [Array<Symbol|String>] columns
|
7
|
+
def initialize(*columns)
|
8
|
+
super
|
7
9
|
@array = []
|
8
10
|
end
|
9
11
|
|
@@ -98,6 +100,25 @@ module QML
|
|
98
100
|
def pop(count = nil)
|
99
101
|
delete_at(@array.size - count, count)
|
100
102
|
end
|
103
|
+
|
104
|
+
# Deletes all items.
|
105
|
+
# @return [self]
|
106
|
+
def clear
|
107
|
+
removing(0 ... count) do
|
108
|
+
@array.clear
|
109
|
+
end
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
# Replaces entire array with given array.
|
114
|
+
# @param [Array] ary
|
115
|
+
# @return [self]
|
116
|
+
def replace(ary)
|
117
|
+
resetting do
|
118
|
+
@array = ary.dup
|
119
|
+
end
|
120
|
+
self
|
121
|
+
end
|
101
122
|
end
|
102
123
|
end
|
103
124
|
end
|