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