qml 0.0.1 → 0.0.2

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/Gemfile +2 -0
  3. data/README.md +82 -19
  4. data/changes.md +17 -0
  5. data/examples/fizzbuzz/capture.png +0 -0
  6. data/examples/todo_array/capture.png +0 -0
  7. data/examples/{todo → todo_array}/main.qml +1 -1
  8. data/examples/{todo/todo.rb → todo_array/todo_array.rb} +4 -7
  9. data/examples/todo_sequel/capture.png +0 -0
  10. data/examples/todo_sequel/main.qml +87 -0
  11. data/examples/todo_sequel/todo_sequel.rb +66 -0
  12. data/examples/twitter/capture.png +0 -0
  13. data/examples/twitter/main.qml +31 -19
  14. data/examples/twitter/twitter.rb +46 -23
  15. data/ext/qml/accessclass.cpp +1 -3
  16. data/ext/qml/ext_anywrapper.cpp +36 -0
  17. data/ext/qml/ext_anywrapper.h +26 -0
  18. data/ext/qml/ext_metaobject.cpp +3 -1
  19. data/ext/qml/init.cpp +3 -3
  20. data/ext/qml/listmodel.cpp +29 -4
  21. data/ext/qml/listmodel.h +2 -0
  22. data/ext/qml/util.cpp +7 -0
  23. data/ext/qml/util.h +1 -0
  24. data/ext/qml/weakvaluereference.cpp +34 -9
  25. data/ext/qml/weakvaluereference.h +7 -5
  26. data/lib/qml/class_builder.rb +1 -0
  27. data/lib/qml/data.rb +1 -0
  28. data/lib/qml/data/array_model.rb +23 -2
  29. data/lib/qml/data/list_model.rb +26 -26
  30. data/lib/qml/data/query_model.rb +60 -0
  31. data/lib/qml/version.rb +1 -1
  32. data/qml.gemspec +5 -1
  33. data/spec/qml/data/array_model_spec.rb +43 -56
  34. data/spec/qml/data/list_model_spec.rb +17 -0
  35. data/spec/qml/data/query_model_spec.rb +62 -0
  36. data/spec/shared_examples/qml/data/list_model.rb +33 -0
  37. data/spec/spec_helper.rb +7 -0
  38. metadata +70 -30
  39. data/ext/qml/ext_gcmarker.cpp +0 -39
  40. data/ext/qml/ext_gcmarker.h +0 -27
  41. data/spec/qml/.access_spec.rb.swp +0 -0
@@ -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 TwitterModel < QML::Data::ArrayModel
11
- column :tweet_text, :user_name, :user_icon
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, TwitterModel.new
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
@@ -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
- rescue([&] {
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
@@ -106,7 +106,9 @@ public:
106
106
  bool voidReturning = (returnType == QMetaType::Void);
107
107
  QVariant returnValue;
108
108
  if (!voidReturning) {
109
- returnValue = QVariant(returnType, QMetaType::create(returnType));
109
+ auto data = QMetaType::create(returnType);
110
+ returnValue = QVariant(returnType, data);
111
+ QMetaType::destroy(returnType, data);
110
112
  }
111
113
 
112
114
  bool result;
@@ -1,7 +1,7 @@
1
1
  #include "ext_metaobject.h"
2
2
  #include "ext_pointer.h"
3
3
  #include "ext_pluginloader.h"
4
- #include "ext_gcmarker.h"
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::GCMarker::defineClass();
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::GCMarker::fromMarkFunction([] {
38
+ auto marker = Ext::AnyWrapper::create(QVariant(), [](const QVariant &) {
39
39
  ValueReference::markAllReferences();
40
40
  ObjectGC::instance()->markNonOwnedObjects();
41
41
  });
@@ -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("class").send("columns").to<QList<QByteArray>>();
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
- return mRubyModel.send("[]", RubyValue::from(index.row())).send("[]", RubyValue::fromID(mColumnIDs[role])).to<QVariant>();
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
- return mRubyModel.send("count").to<int>();
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));
@@ -32,6 +32,8 @@ public slots:
32
32
  void endInsert();
33
33
  void beginRemove(int first, int last);
34
34
  void endRemove();
35
+ void beginReset();
36
+ void endReset();
35
37
  void update(int first, int last);
36
38
 
37
39
  private:
@@ -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)
@@ -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
- mHasValue(true),
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, VALUE(this));
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
- VALUE WeakValueReference::finalize(VALUE arg, VALUE data)
42
+ bool WeakValueReference::hasValue() const
43
+ {
44
+ return d->mHasValue;
45
+ }
46
+
47
+ RubyValue WeakValueReference::value() const
19
48
  {
20
- Q_UNUSED(arg);
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 { return mHasValue; }
12
- RubyValue value() const { return mValue; }
12
+ bool hasValue() const;
13
+ RubyValue value() const;
14
+
15
+ struct Data;
16
+
13
17
  private:
14
- static VALUE finalize(VALUE arg, VALUE data);
15
- bool mHasValue;
16
- RubyValue mValue;
18
+ std::shared_ptr<Data> d;
17
19
  };
18
20
 
19
21
  } // namespace RubyQml
@@ -33,6 +33,7 @@ module QML
33
33
  @objptr = objptr
34
34
  @metaobj = metaobj
35
35
  @name = name
36
+ @initialized = false
36
37
  end
37
38
 
38
39
  private
@@ -1,2 +1,3 @@
1
1
  require 'qml/data/list_model'
2
2
  require 'qml/data/array_model'
3
+ require 'qml/data/query_model'
@@ -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
- def initialize
6
- super()
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