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/lib/qml/data/list_model.rb
CHANGED
@@ -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
|
-
|
32
|
-
|
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
|
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
|
data/lib/qml/version.rb
CHANGED
data/qml.gemspec
CHANGED
@@ -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
|
-
|
10
|
-
|
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
|
30
|
+
model.push(*original_array)
|
49
31
|
end
|
50
32
|
end
|
51
33
|
|
52
|
-
|
53
|
-
context[:arrayModel] = model
|
54
|
-
end
|
34
|
+
include_context 'ListView for model available'
|
55
35
|
|
56
|
-
shared_examples '
|
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
|
-
|
133
|
-
|
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
|
-
|
149
|
-
|
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
|
-
|
165
|
-
|
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
|
-
|
179
|
-
|
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 '
|
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
|