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
@@ -12,24 +12,16 @@ module QML
12
12
  include Wrappable
13
13
  include Dispatchable
14
14
 
15
- class << self
16
- # Declares the columns of the model.
17
- # @param [Array<Symbol|String>] columns
18
- def column(*columns)
19
- @columns ||= []
20
- @columns |= columns
21
- end
22
-
23
- # The columns of the model.
24
- attr_reader :columns
25
- end
26
-
27
15
  # @api private
28
16
  # @return [Array<QtObjectBase>]
29
17
  attr_reader :qt_models
30
18
 
31
- def initialize
32
- fail Error, 'columns not specified' unless self.class.columns
19
+ # @return [Array<Symbol|String>]
20
+ attr_reader :columns
21
+
22
+ # @param [Array<Symbol|String>] columns the column names of the model.
23
+ def initialize(*columns)
24
+ @columns = columns
33
25
  @qt_models = []
34
26
  end
35
27
 
@@ -80,17 +72,16 @@ module QML
80
72
  # @see http://qt-project.org/doc/qt-5/qabstractitemmodel.html#endMoveRows QAbstractItemModel::endMoveRows
81
73
  # @see #inserting
82
74
  # @see #removing
83
- def moving(range, destination, &block)
75
+ def moving(range, destination)
76
+ return if range.size == 0
77
+
84
78
  @qt_models.each do |qt_model|
85
79
  qt_model.begin_move(range.min, range.max, destination)
86
80
  end
87
-
88
- ret = block.call
89
-
81
+ ret = yield
90
82
  @qt_models.each do |qt_model|
91
83
  qt_model.end_move
92
84
  end
93
-
94
85
  ret
95
86
  end
96
87
 
@@ -107,16 +98,15 @@ module QML
107
98
  # @see #removing
108
99
  # @see #moving
109
100
  def inserting(range, &block)
101
+ return if range.size == 0
102
+
110
103
  @qt_models.each do |qt_model|
111
104
  qt_model.begin_insert(range.min, range.max)
112
105
  end
113
-
114
- ret = block.call
115
-
106
+ ret = yield
116
107
  @qt_models.each do |qt_model|
117
108
  qt_model.end_insert
118
109
  end
119
-
120
110
  ret
121
111
  end
122
112
 
@@ -129,16 +119,26 @@ module QML
129
119
  # @see #inserting
130
120
  # @see #moving
131
121
  def removing(range, &block)
122
+ return if range.size == 0
123
+
132
124
  @qt_models.each do |qt_model|
133
125
  qt_model.begin_remove(range.min, range.max)
134
126
  end
135
-
136
- ret = block.call
137
-
127
+ ret = yield
138
128
  @qt_models.each do |qt_model|
139
129
  qt_model.end_remove
140
130
  end
131
+ ret
132
+ end
141
133
 
134
+ def resetting(&block)
135
+ @qt_models.each do |qt_model|
136
+ qt_model.begin_reset
137
+ end
138
+ ret = yield
139
+ @qt_models.each do |qt_model|
140
+ qt_model.end_reset
141
+ end
142
142
  ret
143
143
  end
144
144
  end
@@ -0,0 +1,60 @@
1
+ module QML
2
+ module Data
3
+ # {QueryModel} provides a list model implementation with database backends like ActiveRecord.
4
+ class QueryModel < ListModel
5
+ attr_reader :count
6
+
7
+ # @param [Array<Symbol|String>] columns
8
+ def initialize(*columns)
9
+ super
10
+ @count = 0
11
+ @caches = []
12
+ update
13
+ end
14
+
15
+ def [](index)
16
+ block_index = index / CACHE_SIZE
17
+ cache = @caches.find { |c| c.block_index == block_index } || add_cache(block_index)
18
+ cache.items[index % CACHE_SIZE]
19
+ end
20
+
21
+ # Updates the model.
22
+ def update
23
+ @caches = []
24
+ resetting do
25
+ @count = query_count
26
+ end
27
+ end
28
+
29
+ # @abstract
30
+ # Queries the count of the records.
31
+ # Called when {#update} is called and the result is set as the {#count} of the model.
32
+ # @return [Integer]
33
+ def query_count
34
+ fail ::NotImplementedError
35
+ end
36
+
37
+ # @abstract
38
+ # Queries a block of records. The results are chached.
39
+ # @param [Integer] offset
40
+ # @param [Integer] count
41
+ # @return [Array]
42
+ def query(offset, count)
43
+ fail ::NotImplementedError
44
+ end
45
+
46
+ private
47
+
48
+ Cache = Struct.new(:block_index, :items)
49
+ CACHE_SIZE = 256
50
+ CACHE_COUNT = 4
51
+
52
+ def add_cache(block_offset)
53
+ @caches.shift if @caches.size >= CACHE_COUNT
54
+ Cache.new(block_offset, query(block_offset * CACHE_SIZE, CACHE_SIZE)).tap do |cache|
55
+ @caches << cache
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,3 +1,3 @@
1
1
  module QML
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -19,10 +19,14 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
  spec.extensions = ["ext/qml/extconf.rb"]
21
21
 
22
+ spec.required_ruby_version = '~> 2.0'
23
+
22
24
  spec.add_development_dependency "bundler", "~> 1.5"
23
25
  spec.add_development_dependency "rake", "~> 10.3"
24
26
  spec.add_development_dependency "rspec", "~> 3.0"
25
27
  spec.add_development_dependency 'pry'
26
28
  spec.add_development_dependency 'celluloid'
27
- spec.add_development_dependency 'twitter'
29
+ spec.add_development_dependency 'twitter', '~> 5.11'
30
+ spec.add_development_dependency 'sequel', '~> 4.12'
31
+ spec.add_development_dependency 'sqlite3', '~> 1.3'
28
32
  end
@@ -2,30 +2,12 @@ require 'spec_helper'
2
2
 
3
3
  describe QML::Data::ArrayModel do
4
4
 
5
- class NoColumnArrayModel < QML::Data::ArrayModel
6
- end
7
-
8
5
  class TestArrayModel < QML::Data::ArrayModel
9
- column :title, :number
10
- end
11
-
12
- let(:context) { QML::Context.new }
13
-
14
- let(:component) do
15
- QML::Component.new context: context, data: <<-EOS
16
- import QtQuick 2.0
17
- ListView {
18
- model: arrayModel
19
- delegate: Item {
20
- property var itemTitle: title
21
- property var itemNumber: number
22
- }
23
- }
24
- EOS
6
+ def initialize
7
+ super(:title, :number)
8
+ end
25
9
  end
26
10
 
27
- let(:list_view) { component.create }
28
-
29
11
  let(:original_array) do
30
12
  [
31
13
  {title: 'hoge', number: 12},
@@ -45,41 +27,18 @@ describe QML::Data::ArrayModel do
45
27
 
46
28
  let(:model) do
47
29
  TestArrayModel.new.tap do |model|
48
- model.push *original_array
30
+ model.push(*original_array)
49
31
  end
50
32
  end
51
33
 
52
- before do
53
- context[:arrayModel] = model
54
- end
34
+ include_context 'ListView for model available'
55
35
 
56
- shared_examples 'model content' do |text|
36
+ shared_examples 'same as expected array' do |text|
57
37
  it text do
58
38
  expect(model.to_a).to eq(expected_array)
59
39
  end
60
40
  end
61
41
 
62
- shared_examples 'ListView' do
63
- it 'updates QML ListView correctly' do
64
- count = list_view.count
65
- expect(count).to eq expected_array.size
66
- list_view.count.times do |i|
67
- list_view.current_index = i
68
- current = list_view.current_item
69
- expect(current.item_title).to eq(expected_array[i][:title])
70
- expect(current.item_number).to eq(expected_array[i][:number])
71
- end
72
- end
73
- end
74
-
75
- describe '#initialize' do
76
- context 'when columns are not specified' do
77
- it 'fails' do
78
- expect { NoColumnArrayModel.new }.to raise_error(QML::Data::Error)
79
- end
80
- end
81
- end
82
-
83
42
  describe '#each' do
84
43
  context 'with no block' do
85
44
  it 'returns an Enumerator' do
@@ -129,8 +88,8 @@ describe QML::Data::ArrayModel do
129
88
  model[2] = additional_array[0]
130
89
  expected_array[2] = additional_array[0]
131
90
  end
132
- include_examples 'model content', 'sets the element to given index'
133
- include_examples 'ListView'
91
+ it_behaves_like 'same as expected array', 'sets the element to given index'
92
+ it_behaves_like 'ListView data source'
134
93
  end
135
94
  end
136
95
 
@@ -145,8 +104,8 @@ describe QML::Data::ArrayModel do
145
104
  model.insert(1, *additional_array)
146
105
  expected_array.insert(1, *additional_array)
147
106
  end
148
- include_examples 'model content', 'inserts item'
149
- include_examples 'ListView'
107
+ it_behaves_like 'same as expected array', 'inserts item'
108
+ it_behaves_like 'ListView data source'
150
109
  end
151
110
  end
152
111
 
@@ -161,8 +120,8 @@ describe QML::Data::ArrayModel do
161
120
  model.delete_at(1)
162
121
  expected_array.delete_at(1)
163
122
  end
164
- include_examples 'model content', 'deletes item'
165
- include_examples 'ListView'
123
+ it_behaves_like 'same as expected array', 'deletes item'
124
+ it_behaves_like 'ListView data source'
166
125
  end
167
126
  end
168
127
 
@@ -175,8 +134,8 @@ describe QML::Data::ArrayModel do
175
134
  model.delete_at(1, 2)
176
135
  2.times { expected_array.delete_at(1) }
177
136
  end
178
- include_examples 'model content', 'deletes items'
179
- include_examples 'ListView'
137
+ it_behaves_like 'same as expected array', 'deletes items'
138
+ it_behaves_like 'ListView data source'
180
139
  end
181
140
  end
182
141
  end
@@ -207,9 +166,37 @@ describe QML::Data::ArrayModel do
207
166
  end
208
167
  end
209
168
 
210
- describe '<<' do
169
+ describe '#<<' do
211
170
  it 'is an alias of #push' do
212
171
  expect((model << additional_array[0]).to_a).to eq(expected_array << additional_array[0])
213
172
  end
214
173
  end
174
+
175
+ describe '#clear' do
176
+ it 'returns self' do
177
+ expect(model.clear).to be(model)
178
+ end
179
+ context 'after called' do
180
+ before do
181
+ model.clear
182
+ expected_array.clear
183
+ end
184
+ it_behaves_like 'same as expected array', 'clears items'
185
+ it_behaves_like 'ListView data source'
186
+ end
187
+ end
188
+
189
+ describe '#replace' do
190
+ it 'returns self' do
191
+ expect(model.replace(expected_array + additional_array)).to be(model)
192
+ end
193
+ context 'after called' do
194
+ before do
195
+ model.replace(expected_array + additional_array)
196
+ expected_array.push *additional_array
197
+ end
198
+ it_behaves_like 'same as expected array', 'replaces entire array'
199
+ it_behaves_like 'ListView data source'
200
+ end
201
+ end
215
202
  end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe QML::Data::ListModel do
4
+ let(:model) { QML::Data::ListModel.allocate }
5
+
6
+ describe '#count' do
7
+ it 'fails with NotImplementedError by default' do
8
+ expect { model.count }.to raise_error(NotImplementedError)
9
+ end
10
+ end
11
+
12
+ describe '#[]' do
13
+ it 'fails with NotImplementedError by default' do
14
+ expect { model[0] }.to raise_error(NotImplementedError)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe QML::Data::QueryModel do
4
+
5
+ let(:klass) do
6
+ Class.new(QML::Data::QueryModel) do
7
+ attr_accessor :data
8
+
9
+ def initialize(count)
10
+ @data = count.times.map { |i| {title: "title: #{i}", number: i} }
11
+ super(:title, :number)
12
+ end
13
+
14
+ def query(offset, count)
15
+ @data[offset ... offset + count]
16
+ end
17
+
18
+ def query_count
19
+ @data.size
20
+ end
21
+ end
22
+ end
23
+
24
+ let(:model) { klass.new(2000) }
25
+ let(:expected_array) { model.data }
26
+
27
+ describe '#count' do
28
+ it 'returns the count and updated by #update' do
29
+ count = model.data.size
30
+ expect(model.count).to eq(count)
31
+ model.data << {value: 0}
32
+ expect(model.count).to eq(count)
33
+ model.update
34
+ expect(model.count).to eq(count + 1)
35
+ end
36
+ end
37
+
38
+ describe '#[]' do
39
+ it 'returns the item' do
40
+ model.data.size.times do |i|
41
+ expect(model[i]).to eq(model.data[i])
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '#query_count' do
47
+ it 'fails with NotImplementedError by default' do
48
+ expect { QML::Data::QueryModel.allocate.query_count }.to raise_error(NotImplementedError)
49
+ end
50
+ end
51
+
52
+ describe '#query' do
53
+ it 'fails with NotImplementedError by default' do
54
+ expect { QML::Data::QueryModel.allocate.query(0, 100) }.to raise_error(NotImplementedError)
55
+ end
56
+ end
57
+
58
+ include_context 'ListView for model available'
59
+ it_behaves_like 'ListView data source' do
60
+ let(:model) { klass.new(10) }
61
+ end
62
+ end
@@ -0,0 +1,33 @@
1
+ shared_context 'ListView for model available' do
2
+ let(:context) { QML::Context.new }
3
+ let(:component) do
4
+ QML::Component.new context: context, data: <<-EOS
5
+ import QtQuick 2.0
6
+ ListView {
7
+ model: the_model
8
+ delegate: Item {
9
+ property var itemTitle: title
10
+ property var itemNumber: number
11
+ }
12
+ }
13
+ EOS
14
+ end
15
+ let(:list_view) { component.create }
16
+
17
+ before do
18
+ context[:the_model] = model
19
+ end
20
+ end
21
+
22
+ shared_examples 'ListView data source' do
23
+ it 'updates ListView correctly' do
24
+ count = list_view.count
25
+ expect(count).to eq expected_array.size
26
+ list_view.count.times do |i|
27
+ list_view.current_index = i
28
+ current = list_view.current_item
29
+ expect(current.item_title).to eq(expected_array[i][:title])
30
+ expect(current.item_number).to eq(expected_array[i][:number])
31
+ end
32
+ end
33
+ end