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.
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