ruby-qt6-rice 1.0.0

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.
data/lib/qt6/rspec.rb ADDED
@@ -0,0 +1,275 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rspec/bando_file_parser"
4
+ require_relative "rspec/qlass_file_parser"
5
+
6
+ module RubyQt6
7
+ module RSpec
8
+ VERIFY_QLASS_VIRTUAL_METHODS = [
9
+ "event", "event_filter", # QObject
10
+ "meta_object", # QObject
11
+ "connect_notify", "disconnect_notify", # QObject
12
+ "paint_engine", # QPaintDevice
13
+ "metric", # QPaintDevice
14
+ "has_height_for_width", "height_for_width", # QWidget
15
+ "input_method_query", # QWidget
16
+ "minimum_size_hint", "size_hint", # QWidget
17
+ "init_painter" # QWidget
18
+ ]
19
+
20
+ NO_VERIFY_QLASS_DOCS = [
21
+ "QMetaMethodBuilder",
22
+ "QMetaObjectBuilder"
23
+ ]
24
+ NO_VERIFY_QLASS_INITIALIZE = QlassFileParser::NESTED_QLASSES + [
25
+ "QVariant"
26
+ ]
27
+ NO_VERIFY_QLASS_VIRTUAL_METHODS = {
28
+ "QCoreApplication" => ["post_event", "send_event"],
29
+ "QLayoutItem" => ["size_hint"],
30
+ "QWidgetItem" => ["has_height_for_width", "height_for_width", "size_hint"],
31
+ "QWidget" => ["minimum_size_hint", "size_hint"]
32
+ }
33
+ NO_VERIFY_QLASS_LEADING_UNDERSCORE_METHODS = {
34
+ "QObject" => ["_connect", "_disconnect"],
35
+ "QApplication" => ["_all_widgets", "_top_level_widgets"]
36
+ }
37
+ NO_VERIFY_QLASS_OPERATOR_METHODS = {
38
+ "QTextStream" => ["<<"],
39
+ "QString" => ["[]", "[]="],
40
+ "QPoint" => ["*", "+", "-", "/", "+@", "-@"],
41
+ "QPointF" => ["*", "+", "-", "/", "+@", "-@"],
42
+ "QSize" => ["*", "+", "-", "/"],
43
+ "QSizeF" => ["*", "+", "-", "/"]
44
+ }
45
+ NO_VERIFY_QLASS_QOBJECT_INITIALIZE = [
46
+ "QCoreApplication",
47
+ "QGuiApplication",
48
+ "QApplication",
49
+ "QSettings",
50
+ "QAction",
51
+ "QDoubleValidator",
52
+ "QIntValidator",
53
+ "QRegularExpressionValidator",
54
+ "QGraphicsTextItem",
55
+ "QLabel",
56
+ "QRadioButton",
57
+ "QLineEdit",
58
+ "QTextEdit",
59
+ "QSystemTrayIcon"
60
+ ]
61
+
62
+ ACRONYM = [
63
+ "UInt",
64
+ "ULongLong",
65
+ "ULong",
66
+ "UShort",
67
+ "LongLong",
68
+ "MSecs",
69
+ "QBitmap",
70
+ "QMetaObject",
71
+ "QObject"
72
+ ]
73
+
74
+ def self.inflector_underscore(str)
75
+ str = str.dup
76
+ ACRONYM.each { |acronym| str.gsub!(acronym, "_" + acronym.downcase) }
77
+ str.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
78
+ str.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
79
+ str.downcase!
80
+ str
81
+ end
82
+
83
+ def self.verify_bando_cppfile(cppfile, qmod)
84
+ parser = BandoFileParser.new(cppfile, qmod)
85
+ bandoes = parser.parse
86
+
87
+ rs = {}
88
+ bandoes.each do |bando|
89
+ r = Struct.new(:bando).new(bando)
90
+ rs[bando.name] = r
91
+
92
+ rbfile = "lib/qt6/bando/#{bando.name.downcase}.rb"
93
+ rbfile_contents = File.read(rbfile) if File.exist?(rbfile)
94
+
95
+ raise rbfile unless rbfile_contents.include?("class #{bando.name} < RubyQt6::#{qmod.name}::#{bando.name}")
96
+ raise rbfile unless rbfile_contents.match?(/Bando.define_bando_q\w+ Bando::#{bando.name}/)
97
+ end
98
+ rs
99
+ end
100
+
101
+ def self.verify_qlass_cppfile(cppfile, qmod)
102
+ parser = QlassFileParser.new(cppfile, qmod)
103
+ qlasses = parser.parse
104
+
105
+ rs = {}
106
+ qlasses.each do |qlass|
107
+ r = Struct.new(:qlass, :verified_initialize, :verified_methods_count, :verified_enums_count, :verified_flags_count).new(qlass, 0, 0, 0, 0)
108
+ rs[qlass.name] = r
109
+
110
+ rbfile = "lib/qt6/#{qmod.name.downcase}/#{qlass.name.downcase}.rb"
111
+ rbfile_contents = File.read(rbfile) if File.exist?(rbfile)
112
+
113
+ verify_qlass_cppfile_qlass_docs(qlass, cppfile, rbfile, rbfile_contents, r)
114
+ verify_qlass_cppfile_qlass_initialize(qlass, cppfile, rbfile, rbfile_contents, r)
115
+ verify_qlass_cppfile_qlass_methods(qlass, cppfile, rbfile, rbfile_contents, r)
116
+ verify_qlass_cppfile_qlass_enums(qlass, cppfile, rbfile, rbfile_contents, r)
117
+ verify_qlass_cppfile_qlass_flags(qlass, cppfile, rbfile, rbfile_contents, r)
118
+
119
+ qobject = true if rbfile_contents&.include?("q_object")
120
+ verify_qlass_cppfile_qlass_qobject_initialize(qlass, cppfile, rbfile, rbfile_contents, r) if qobject
121
+ verify_qlass_cppfile_qlass_qobject_methods(qlass, cppfile, rbfile, rbfile_contents, r) if qobject
122
+ end
123
+ rs
124
+ end
125
+
126
+ def self.verify_qlass_cppfile_qlass_docs(qlass, cppfile, rbfile, rbfile_contents, r)
127
+ return if NO_VERIFY_QLASS_DOCS.include?(qlass.name)
128
+
129
+ return if rbfile_contents.nil?
130
+ return if rbfile_contents.include?("# @see https://doc.qt.io/qt-6/#{qlass.name.downcase}.html")
131
+ raise "#{rbfile}: docs: Missing `# @see https://doc.qt.io/qt-6/...`"
132
+ end
133
+
134
+ def self.verify_qlass_cppfile_qlass_initialize(qlass, cppfile, rbfile, rbfile_contents, r)
135
+ return if NO_VERIFY_QLASS_INITIALIZE.include?(qlass.name)
136
+
137
+ constructor_methods = qlass.methods.select { |method| method.type == :constructor }
138
+ copy_constructor_method = constructor_methods.find { |method| method.rawline.include?('Arg("other")') }
139
+
140
+ constructor_methods.each do |method|
141
+ rawline = method.rawline
142
+ raise "#{cppfile}: Found default value in constructor method" if rawline.include?("nullptr")
143
+ raise "#{cppfile}: Found default value in constructor method" if rawline.include?("static_cast")
144
+ end
145
+
146
+ constructor_methods_count = constructor_methods.count
147
+ constructor_methods_count -= 1 if copy_constructor_method
148
+ case constructor_methods_count
149
+ when 2..99
150
+ raise "#{rbfile}: initialize: Missing `# @overload initialize...`" unless rbfile_contents.scan("# @overload initialize").count == constructor_methods_count
151
+ raise "#{rbfile}: initialize: Missing `alias_method :_initialize, :initialize`" unless rbfile_contents.include?("alias_method :_initialize, :initialize")
152
+ raise "#{rbfile}: initialize: Missing `def initialize(*args)`" unless rbfile_contents.include?("def initialize(*args)")
153
+ raise "#{rbfile}: initialize: Missing `_initialize(*args)`" unless rbfile_contents.include?("_initialize(*args)")
154
+ r.verified_initialize = 2
155
+ when 1
156
+ raise "#{rbfile}: initialize: Missing `alias_method :_initialize, :initialize`" unless rbfile_contents.include?("alias_method :_initialize, :initialize")
157
+ raise "#{rbfile}: initialize: Missing `def initialize...`" unless rbfile_contents.include?("def initialize")
158
+ raise "#{rbfile}: initialize: Missing `_initialize...`" unless rbfile_contents.include?("_initialize")
159
+ r.verified_initialize = 1
160
+ end
161
+ end
162
+
163
+ def self.verify_qlass_cppfile_qlass_methods(qlass, cppfile, rbfile, rbfile_contents, r)
164
+ qlass.methods.each do |method|
165
+ # Expected 'Arg("name")', but got 'Arg("")'
166
+ if method.rawline.include?('Arg("")')
167
+ raise method.inspect
168
+ end
169
+
170
+ # Expected 'to_qvariant', but got 'to_q_variant'
171
+ if method.rbname.include?("q_")
172
+ raise method.inspect
173
+ end
174
+
175
+ # Expected 'valid?', but got 'is_valid'
176
+ # Expected 'user_type', but got 'userType'
177
+ if method.cppname
178
+ expected =
179
+ case method.cppname
180
+ when /^is[A-Z]/ then "#{inflector_underscore(method.cppname)}?".sub(/^is_/, "")
181
+ else inflector_underscore(method.cppname)
182
+ end
183
+ if expected != method.rbname.sub(/^_/, "")
184
+ raise method.inspect
185
+ end
186
+ end
187
+
188
+ # No virtual method
189
+ lambda do
190
+ return if NO_VERIFY_QLASS_VIRTUAL_METHODS[qlass.name]&.include?(method.rbname)
191
+ raise method.inspect if method.rbname.end_with?("_event")
192
+ raise method.inspect if VERIFY_QLASS_VIRTUAL_METHODS.include?(method.rbname)
193
+ end.call
194
+
195
+ # No unknonwn leading underscore method
196
+ if method.rbname.start_with?("_")
197
+ lambda do
198
+ return if method.type == :rubyqt6_defined_functions
199
+ return if rbfile_contents.match?(/def (self.)?#{method.rbname.sub(/^_/, "")}/)
200
+ return if NO_VERIFY_QLASS_LEADING_UNDERSCORE_METHODS[qlass.name]&.include?(method.rbname)
201
+ raise method.inspect
202
+ end.call
203
+ end
204
+
205
+ # No unknonwn operator method
206
+ unless method.rbname.start_with?(/_?[a-z]/)
207
+ lambda do
208
+ return if NO_VERIFY_QLASS_OPERATOR_METHODS[qlass.name]&.include?(method.rbname)
209
+ raise method.inspect
210
+ end.call
211
+ end
212
+
213
+ r.verified_methods_count ||= 0
214
+ r.verified_methods_count += 1
215
+ end
216
+ end
217
+
218
+ def self.verify_qlass_cppfile_qlass_enums(qlass, cppfile, rbfile, rbfile_contents, r)
219
+ qlass.enums.each do |enum|
220
+ raise "#{rbfile}: #{enum.name}: Missing `# @!parse class ...`" unless rbfile_contents.include?("# @!parse class #{enum.name}")
221
+ raise "#{rbfile}: #{enum.name}: Missing `rubyqt6_declare_enum_under ...`" unless rbfile_contents.include?("rubyqt6_declare_enum_under #{qlass.name}, #{qlass.name}::#{enum.name}")
222
+ r.verified_enums_count ||= 0
223
+ r.verified_enums_count += 1
224
+ end
225
+ end
226
+
227
+ def self.verify_qlass_cppfile_qlass_flags(qlass, cppfile, rbfile, rbfile_contents, r)
228
+ qlass.flags.each do |flag|
229
+ raise "#{rbfile}: #{flag.name}: Missing `# @!parse class ...`" unless rbfile_contents.include?("# @!parse class #{flag.name}")
230
+ raise "#{rbfile}: #{flag.name}: Missing `rubyqt6_declare_qflags ...`" unless rbfile_contents.include?("rubyqt6_declare_qflags #{qlass.name}::#{flag.name}, #{qlass.name}::#{flag.enum_name}")
231
+ r.verified_flags_count ||= 0
232
+ r.verified_flags_count += 1
233
+ end
234
+ end
235
+
236
+ def self.verify_qlass_cppfile_qlass_qobject_initialize(qlass, cppfile, rbfile, rbfile_contents, r)
237
+ constructor_methods = qlass.methods.select { |method| method.type == :constructor }
238
+ return if constructor_methods.size == 0
239
+ raise "#{cppfile}: Too many constructor methods" if constructor_methods.size > 1
240
+
241
+ unless NO_VERIFY_QLASS_QOBJECT_INITIALIZE.include?(qlass.name)
242
+ rawline = constructor_methods[0].rawline
243
+ rawargs = rawline.scan(/Arg\("(\w+)"\)/).map { |arg| arg[0] }
244
+ rawargs.each do |arg|
245
+ line = "@param #{arg} ["
246
+ raise "#{rbfile}: initialize: Missing `#{line}`" unless rbfile_contents.include?(line)
247
+ end
248
+
249
+ line = "@return [#{qlass.name}]"
250
+ raise "#{rbfile}: initialize: Missing `#{line}`" unless rbfile_contents.include?(line)
251
+
252
+ args = rawargs.map { |arg| "#{arg}( = .*)?" }
253
+ line = args.size.zero? ? /def initialize/ : /def initialize\(#{args.join(", ")}\)/
254
+ raise "#{rbfile}: initialize: Mismatch `#{line}`" unless rbfile_contents.match?(line)
255
+
256
+ args = rawargs.map { |arg| ".*#{arg}.*" }
257
+ line = args.size.zero? ? /_initialize/ : /_initialize\(#{args.join(", ")}\)/
258
+ raise "#{rbfile}: initialize: Mismatch `#{line}`" unless rbfile_contents.match?(line)
259
+ end
260
+ end
261
+
262
+ def self.verify_qlass_cppfile_qlass_qobject_methods(qlass, cppfile, rbfile, rbfile_contents, r)
263
+ metamethods = [].tap do |methods|
264
+ signal_methods = qlass.methods.select { |method| method.type == :signals }
265
+ signal_methods.each { |method| methods << method.cppname }
266
+ slot_methods = qlass.methods.select { |method| method.type == :public_slots }
267
+ slot_methods.each { |method| methods << method.cppname }
268
+ end.uniq
269
+
270
+ rbfile_metamethods = rbfile_contents.scan(/(signal|slot) "(.*)"/).map { |method| method[1] }
271
+ rbfile_metamethods = rbfile_metamethods.map { |method| method.split("(")[0] }.uniq
272
+ raise "#{rbfile}: q_object: Mismatch `#{rbfile_metamethods}` != `#{metamethods}`" if rbfile_metamethods != metamethods
273
+ end
274
+ end
275
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-qt6-rice
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - John Doe
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Extend the Rice library with support for the Qt framework.
13
+ email:
14
+ - johndoe@example.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - ".rspec"
20
+ - ".rubocop.yml"
21
+ - LICENSE
22
+ - README.md
23
+ - Rakefile
24
+ - include/bando/common.hpp
25
+ - include/bando/qobject.hpp
26
+ - include/bando/qobject/qdbusabstractadaptor.hpp
27
+ - include/bando/qobject/qdbusabstractinterface.hpp
28
+ - include/bando/qobject/qitemdelegate.hpp
29
+ - include/bando/qobject/qlayout.hpp
30
+ - include/bando/qwidget.hpp
31
+ - include/bando/qwidget/qspinbox.hpp
32
+ - include/rice/qt6/qdbusreply.hpp
33
+ - include/rice/qt6/qenum.hpp
34
+ - include/rice/qt6/qflags.hpp
35
+ - include/rice/qt6/qlist.hpp
36
+ - include/rice/rice.hpp
37
+ - include/rice/stl.hpp
38
+ - lib/mkmf-rice.rb
39
+ - lib/mkmf-rubyqt6.rb
40
+ - lib/qt6/rice.rb
41
+ - lib/qt6/rice/version.rb
42
+ - lib/qt6/rspec.rb
43
+ - lib/qt6/rspec/bando_file_parser.rb
44
+ - lib/qt6/rspec/qlass_file_parser.rb
45
+ homepage: https://github.com/souk4711/ruby-qt6/rice
46
+ licenses: []
47
+ metadata:
48
+ homepage_uri: https://github.com/souk4711/ruby-qt6/rice
49
+ source_code_uri: https://github.com/souk4711/ruby-qt6/rice
50
+ changelog_uri: https://github.com/souk4711/ruby-qt6/rice
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 3.1.0
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubygems_version: 3.6.9
66
+ specification_version: 4
67
+ summary: Extend the Rice library with support for the Qt framework.
68
+ test_files: []