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