metaruby 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -3
- data/Gemfile +6 -0
- data/Rakefile +12 -2
- data/lib/metaruby.rb +2 -0
- data/lib/metaruby/attributes.rb +52 -60
- data/lib/metaruby/backward/singleton_class_p.rb +14 -0
- data/lib/metaruby/dsls.rb +31 -1
- data/lib/metaruby/dsls/doc.rb +33 -25
- data/lib/metaruby/dsls/find_through_method_missing.rb +78 -16
- data/lib/metaruby/gui.rb +1 -0
- data/lib/metaruby/gui/html/exception_view.css +4 -4
- data/lib/metaruby/gui/html/page_body.rhtml +0 -3
- data/lib/metaruby/gui/model_browser.rb +35 -20
- data/lib/metaruby/gui/model_hierarchy.rb +267 -0
- data/lib/metaruby/gui/model_selector.rb +63 -40
- data/lib/metaruby/model_as_class.rb +9 -13
- data/lib/metaruby/model_as_module.rb +2 -5
- data/lib/metaruby/registration.rb +25 -19
- data/lib/metaruby/test.rb +3 -0
- data/lib/metaruby/version.rb +1 -1
- metadata +5 -4
@@ -18,11 +18,14 @@ module MetaRuby
|
|
18
18
|
# @return [Qt::SortFilterProxyModel]
|
19
19
|
attr_reader :model_filter
|
20
20
|
|
21
|
-
#
|
21
|
+
# A mapping from a root model and the user-visible name for this
|
22
|
+
# root
|
23
|
+
#
|
24
|
+
# @return [Hash<Object,String>]
|
22
25
|
attr_reader :type_info
|
23
26
|
|
24
27
|
# The Qt item model that represents the object hierarchy
|
25
|
-
# @return [
|
28
|
+
# @return [ModelHierarchy]
|
26
29
|
attr_reader :browser_model
|
27
30
|
|
28
31
|
# @return [Qt::PushButton] a button allowing to filter models by
|
@@ -41,9 +44,7 @@ module MetaRuby
|
|
41
44
|
super
|
42
45
|
|
43
46
|
@type_info = Hash.new
|
44
|
-
@browser_model =
|
45
|
-
model?(mod)
|
46
|
-
end
|
47
|
+
@browser_model = ModelHierarchy.new
|
47
48
|
@type_filters = Hash.new
|
48
49
|
|
49
50
|
layout = Qt::VBoxLayout.new(self)
|
@@ -65,12 +66,13 @@ module MetaRuby
|
|
65
66
|
# objects of this type
|
66
67
|
# @param [Integer] priority if an object's ancestry matches multiple
|
67
68
|
# types, only the ones of the highest priority will be retained
|
68
|
-
def register_type(
|
69
|
-
|
69
|
+
def register_type(root_model, name, priority = 0, categories: [], resolver: ModelHierarchy::Resolver.new)
|
70
|
+
@browser_model.add_root(root_model, priority, categories: categories, resolver: resolver)
|
71
|
+
type_info[root_model] = name
|
70
72
|
action = Qt::Action.new(name, self)
|
71
73
|
action.checkable = true
|
72
74
|
action.checked = true
|
73
|
-
type_filters[
|
75
|
+
type_filters[root_model] = action
|
74
76
|
btn_type_filter_menu.add_action(action)
|
75
77
|
connect(action, SIGNAL('triggered()')) do
|
76
78
|
update_model_filter
|
@@ -79,8 +81,13 @@ module MetaRuby
|
|
79
81
|
|
80
82
|
# Update the view, reloading the underlying model
|
81
83
|
def update
|
82
|
-
update_model_filter
|
83
84
|
reload
|
85
|
+
update_model_filter
|
86
|
+
end
|
87
|
+
|
88
|
+
# (see ModelHierarchy#find_resolver_from_model)
|
89
|
+
def find_resolver_from_model(model)
|
90
|
+
@browser_model.find_resolver_from_model(model)
|
84
91
|
end
|
85
92
|
|
86
93
|
# @api private
|
@@ -89,10 +96,10 @@ module MetaRuby
|
|
89
96
|
def update_model_filter
|
90
97
|
type_rx = type_filters.map do |model_base, act|
|
91
98
|
if act.checked?
|
92
|
-
type_info[model_base]
|
99
|
+
type_info[model_base]
|
93
100
|
end
|
94
101
|
end
|
95
|
-
type_rx = type_rx.compact.join("
|
102
|
+
type_rx = type_rx.compact.join(",|,")
|
96
103
|
|
97
104
|
model_filter.filter_role = Qt::UserRole # this contains the keywords (ancestry and/or name)
|
98
105
|
# This workaround a problem that I did not have time to
|
@@ -104,10 +111,38 @@ module MetaRuby
|
|
104
111
|
# The pattern has to match every element in the hierarchy. We
|
105
112
|
# achieve this by making the suffix part optional
|
106
113
|
name_rx = filter_box.text.downcase.gsub(/:+/, "/")
|
107
|
-
|
114
|
+
name_rx = '[^;]*,[^,]*' + name_rx.split('/').join("[^,]*,[^;]*;[^;]*,") + '[^,]*,[^;]*'
|
115
|
+
regexp = Qt::RegExp.new("(,#{type_rx},)[^;]*;([^;]*;)*#{name_rx}")
|
116
|
+
regexp.case_sensitivity = Qt::CaseInsensitive
|
117
|
+
model_filter.filter_reg_exp = regexp
|
118
|
+
model_filter.invalidate
|
108
119
|
auto_open
|
109
120
|
end
|
110
121
|
|
122
|
+
def filter_row_count(parent = Qt::ModelIndex.new)
|
123
|
+
model_filter.row_count(parent)
|
124
|
+
end
|
125
|
+
|
126
|
+
def model_items_from_filter(parent = Qt::ModelIndex.new)
|
127
|
+
(0...filter_row_count(parent)).map do |i|
|
128
|
+
model_item_from_filter_row(i, parent)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def model_item_from_filter_row(row, parent = Qt::ModelIndex.new)
|
133
|
+
filter_index = model_filter.index(row, 0, parent)
|
134
|
+
model_index = model_filter.map_to_source(filter_index)
|
135
|
+
return browser_model.item_from_index(model_index), filter_index
|
136
|
+
end
|
137
|
+
|
138
|
+
def dump_filtered_item_model(parent = Qt::ModelIndex.new, indent = "")
|
139
|
+
model_items_from_filter(parent).each_with_index do |(model_item, filter_index), i|
|
140
|
+
data = model_item.data(Qt::UserRole).to_string
|
141
|
+
puts "#{indent}[#{i}] #{model_item.text} #{data}"
|
142
|
+
dump_filtered_item_model(filter_index, indent + " ")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
111
146
|
# Auto-open in the current state
|
112
147
|
#
|
113
148
|
# @param [Integer] threshold the method opens items whose number of
|
@@ -135,20 +170,12 @@ module MetaRuby
|
|
135
170
|
end
|
136
171
|
end
|
137
172
|
|
138
|
-
# Tests if an object if a model
|
139
|
-
def model?(obj)
|
140
|
-
type_info.any? do |model_base, _|
|
141
|
-
obj.kind_of?(model_base) ||
|
142
|
-
(obj.kind_of?(Module) && obj <= model_base)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
173
|
class ModelPathCompleter < Qt::Completer
|
147
174
|
def splitPath(path)
|
148
175
|
path.split('/')
|
149
176
|
end
|
150
177
|
def pathFromIndex(index)
|
151
|
-
index.data(Qt::UserRole).split(";").last
|
178
|
+
index.data(Qt::UserRole).to_string.split(";").last
|
152
179
|
end
|
153
180
|
end
|
154
181
|
|
@@ -187,10 +214,11 @@ module MetaRuby
|
|
187
214
|
@model_list = Qt::TreeView.new(self)
|
188
215
|
@model_filter = Qt::SortFilterProxyModel.new
|
189
216
|
model_filter.filter_case_sensitivity = Qt::CaseInsensitive
|
190
|
-
model_filter.dynamic_sort_filter = true
|
191
217
|
model_filter.filter_role = Qt::UserRole
|
192
|
-
|
218
|
+
model_filter.dynamic_sort_filter = true
|
193
219
|
model_filter.source_model = browser_model
|
220
|
+
model_filter.sort(0)
|
221
|
+
model_list.model = model_filter
|
194
222
|
|
195
223
|
@filter_box = Qt::LineEdit.new(self)
|
196
224
|
filter_box.connect(SIGNAL('textChanged(QString)')) do |text|
|
@@ -207,9 +235,8 @@ module MetaRuby
|
|
207
235
|
|
208
236
|
model_list.selection_model.connect(SIGNAL('currentChanged(const QModelIndex&, const QModelIndex&)')) do |index, _|
|
209
237
|
index = model_filter.map_to_source(index)
|
210
|
-
|
211
|
-
|
212
|
-
emit model_selected(Qt::Variant.from_ruby(mod.this, mod.this))
|
238
|
+
if model = browser_model.find_model_from_index(index)
|
239
|
+
emit model_selected(Qt::Variant.from_ruby(model, model))
|
213
240
|
end
|
214
241
|
end
|
215
242
|
end
|
@@ -217,19 +244,15 @@ module MetaRuby
|
|
217
244
|
|
218
245
|
# Reload the object model, keeping the current selection if possible
|
219
246
|
def reload
|
220
|
-
if
|
221
|
-
|
222
|
-
current_path = []
|
223
|
-
while current
|
224
|
-
current_path.unshift current.name
|
225
|
-
current = current.parent
|
226
|
-
end
|
247
|
+
if current_model = current_selection
|
248
|
+
current_path = @browser_model.find_path_from_model(current_model)
|
227
249
|
end
|
228
250
|
|
229
251
|
browser_model.reload
|
230
|
-
|
231
|
-
|
232
|
-
|
252
|
+
if current_path
|
253
|
+
select_by_path(*current_path)
|
254
|
+
elsif current_model
|
255
|
+
select_by_model(current_model)
|
233
256
|
end
|
234
257
|
end
|
235
258
|
|
@@ -258,10 +281,10 @@ module MetaRuby
|
|
258
281
|
if !index
|
259
282
|
return
|
260
283
|
elsif !index.valid?
|
261
|
-
if !
|
284
|
+
if !reset_filter
|
262
285
|
return index
|
263
286
|
end
|
264
|
-
reset_filter
|
287
|
+
self.reset_filter
|
265
288
|
model_filter.map_from_source(source_index)
|
266
289
|
else index
|
267
290
|
end
|
@@ -285,7 +308,7 @@ module MetaRuby
|
|
285
308
|
#
|
286
309
|
# @return [Boolean] true if the path resolved to something known,
|
287
310
|
# and false otherwise
|
288
|
-
def
|
311
|
+
def select_by_model(model)
|
289
312
|
if index = browser_model.find_index_by_model(model)
|
290
313
|
index = map_index_from_source(index)
|
291
314
|
model_list.current_index = index
|
@@ -300,7 +323,7 @@ module MetaRuby
|
|
300
323
|
index = model_list.selection_model.current_index
|
301
324
|
if index.valid?
|
302
325
|
index = model_filter.map_to_source(index)
|
303
|
-
browser_model.
|
326
|
+
browser_model.find_model_from_index(index)
|
304
327
|
end
|
305
328
|
end
|
306
329
|
|
@@ -72,13 +72,15 @@ module MetaRuby
|
|
72
72
|
# assigned on a Ruby constant
|
73
73
|
#
|
74
74
|
# @return [Module] a subclass of self
|
75
|
-
def new_submodel(name: nil, **submodel_options, &block)
|
75
|
+
def new_submodel(name: nil, register: true, **submodel_options, &block)
|
76
76
|
Thread.current[FROM_NEW_SUBMODEL_TLS] = true
|
77
77
|
model = self.class.new(self)
|
78
78
|
model.permanent_model = false
|
79
|
+
model.instance_variable_set :@name, nil
|
80
|
+
model.name = name if name
|
79
81
|
setup_submodel(model, **submodel_options, &block)
|
80
|
-
if
|
81
|
-
model
|
82
|
+
if register
|
83
|
+
register_submodel(model)
|
82
84
|
end
|
83
85
|
model
|
84
86
|
end
|
@@ -90,15 +92,7 @@ module MetaRuby
|
|
90
92
|
|
91
93
|
# Called at the end of the definition of a new submodel
|
92
94
|
def setup_submodel(submodel, register: true, **options, &block)
|
93
|
-
submodel.
|
94
|
-
|
95
|
-
if register
|
96
|
-
register_submodel(submodel)
|
97
|
-
end
|
98
|
-
|
99
|
-
if block_given?
|
100
|
-
submodel.apply_block(&block)
|
101
|
-
end
|
95
|
+
submodel.apply_block(&block) if block
|
102
96
|
end
|
103
97
|
|
104
98
|
# Registers submodels when a subclass is created
|
@@ -108,7 +102,7 @@ module MetaRuby
|
|
108
102
|
|
109
103
|
subclass.definition_location =
|
110
104
|
if MetaRuby.keep_definition_location?
|
111
|
-
|
105
|
+
caller_locations
|
112
106
|
else Array.new
|
113
107
|
end
|
114
108
|
subclass.instance_variable_set :@name, nil
|
@@ -116,7 +110,9 @@ module MetaRuby
|
|
116
110
|
subclass.permanent_model = subclass.accessible_by_name? &&
|
117
111
|
subclass.permanent_definition_context?
|
118
112
|
if !from_new_submodel
|
113
|
+
subclass.instance_variable_set :@name, nil
|
119
114
|
setup_submodel(subclass)
|
115
|
+
register_submodel(subclass)
|
120
116
|
end
|
121
117
|
end
|
122
118
|
|
@@ -121,13 +121,10 @@ module MetaRuby
|
|
121
121
|
def new_submodel(name: nil, type: self.class, **submodel_options, &block)
|
122
122
|
model = type.new
|
123
123
|
model.extend ModelAsModule
|
124
|
-
model.name =
|
125
|
-
if name
|
126
|
-
name.dup
|
127
|
-
end
|
124
|
+
model.name = name.dup if name
|
128
125
|
model.definition_location =
|
129
126
|
if MetaRuby.keep_definition_location?
|
130
|
-
|
127
|
+
caller_locations
|
131
128
|
else Array.new
|
132
129
|
end
|
133
130
|
setup_submodel(model, submodel_options, &block)
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'facets/module/spacename'
|
2
2
|
require 'facets/module/basename'
|
3
|
-
require 'facets/kernel/call_stack'
|
4
3
|
require 'utilrb/object/attribute'
|
5
4
|
require 'utilrb/module/attr_predicate'
|
6
5
|
|
@@ -11,9 +10,9 @@ module MetaRuby
|
|
11
10
|
# returns the model that is parent of +self+
|
12
11
|
module Registration
|
13
12
|
# The place where this model got defined in the source code
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# @return [Array<
|
13
|
+
# This is an array of Thread::Backtrace::Locations
|
14
|
+
#
|
15
|
+
# @return [Array<Thread::Backtrace::Locations>]
|
17
16
|
attr_accessor :definition_location
|
18
17
|
|
19
18
|
# Tells {#clear_submodels} whether this model should be removed from
|
@@ -69,10 +68,14 @@ module MetaRuby
|
|
69
68
|
|
70
69
|
# Call to register a model that is a submodel of +self+
|
71
70
|
def register_submodel(klass)
|
71
|
+
if klass.singleton_class?
|
72
|
+
raise ArgumentError, "cannot register a singleton class"
|
73
|
+
end
|
74
|
+
|
72
75
|
if !klass.definition_location
|
73
76
|
klass.definition_location =
|
74
77
|
if MetaRuby.keep_definition_location?
|
75
|
-
|
78
|
+
caller_locations
|
76
79
|
else Array.new
|
77
80
|
end
|
78
81
|
end
|
@@ -108,12 +111,19 @@ module MetaRuby
|
|
108
111
|
if m = supermodel
|
109
112
|
m.deregister_submodels([self])
|
110
113
|
end
|
111
|
-
|
112
|
-
Registration.deregister_constant(self)
|
113
|
-
end
|
114
|
+
clear_registration_as_constant
|
114
115
|
end
|
115
116
|
clear_submodels
|
116
117
|
end
|
118
|
+
|
119
|
+
# Removes any constant this model is registered as
|
120
|
+
def clear_registration_as_constant
|
121
|
+
# Deregister non-permanent models that are registered in the
|
122
|
+
# constant hierarchy
|
123
|
+
if Registration.accessible_by_name?(self)
|
124
|
+
Registration.deregister_constant(self)
|
125
|
+
end
|
126
|
+
end
|
117
127
|
|
118
128
|
# Removes the constant that stores the given object in the Ruby constant
|
119
129
|
# hierarchy
|
@@ -126,17 +136,13 @@ module MetaRuby
|
|
126
136
|
|
127
137
|
# Recursively deregisters all non-permanent submodels
|
128
138
|
def clear_submodels
|
129
|
-
|
130
|
-
if !
|
131
|
-
deregister_submodels(
|
139
|
+
permanent, non_permanent = each_submodel.partition { |m| m.permanent_model? }
|
140
|
+
if !non_permanent.empty?
|
141
|
+
deregister_submodels(non_permanent)
|
132
142
|
end
|
133
143
|
|
134
|
-
|
135
|
-
|
136
|
-
# constant hierarchy
|
137
|
-
if Registration.accessible_by_name?(m)
|
138
|
-
Registration.deregister_constant(m)
|
139
|
-
end
|
144
|
+
non_permanent.each do |m|
|
145
|
+
m.clear_registration_as_constant
|
140
146
|
end
|
141
147
|
|
142
148
|
# This contains the permanent submodels
|
@@ -144,9 +150,9 @@ module MetaRuby
|
|
144
150
|
# We can call #clear_submodels while iterating here as it is a
|
145
151
|
# constraint that all models in #submodels are permanent (and
|
146
152
|
# will therefore not be removed)
|
147
|
-
|
153
|
+
permanent.each { |m| m.clear_submodels }
|
148
154
|
# And this the non-permanent ones
|
149
|
-
|
155
|
+
non_permanent.each { |m| m.clear_submodels }
|
150
156
|
true
|
151
157
|
end
|
152
158
|
|
data/lib/metaruby/test.rb
CHANGED
data/lib/metaruby/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metaruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sylvain Joyeux
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: utilrb
|
@@ -102,6 +102,7 @@ files:
|
|
102
102
|
- bin/.gitattributes
|
103
103
|
- lib/metaruby.rb
|
104
104
|
- lib/metaruby/attributes.rb
|
105
|
+
- lib/metaruby/backward/singleton_class_p.rb
|
105
106
|
- lib/metaruby/dsls.rb
|
106
107
|
- lib/metaruby/dsls/doc.rb
|
107
108
|
- lib/metaruby/dsls/find_through_method_missing.rb
|
@@ -124,6 +125,7 @@ files:
|
|
124
125
|
- lib/metaruby/gui/html/page_body.rhtml
|
125
126
|
- lib/metaruby/gui/html/rock-website.css
|
126
127
|
- lib/metaruby/gui/model_browser.rb
|
128
|
+
- lib/metaruby/gui/model_hierarchy.rb
|
127
129
|
- lib/metaruby/gui/model_selector.rb
|
128
130
|
- lib/metaruby/gui/rendering_manager.rb
|
129
131
|
- lib/metaruby/gui/ruby_constants_item_model.rb
|
@@ -155,9 +157,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
157
|
version: '0'
|
156
158
|
requirements: []
|
157
159
|
rubyforge_project:
|
158
|
-
rubygems_version: 2.
|
160
|
+
rubygems_version: 2.5.1
|
159
161
|
signing_key:
|
160
162
|
specification_version: 4
|
161
163
|
summary: Modelling using the Ruby language as a metamodel
|
162
164
|
test_files: []
|
163
|
-
has_rdoc:
|