metaruby 1.0.0.rc2 → 1.0.0.rc3
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/.gitignore +1 -0
- data/.travis.yml +4 -0
- data/README.md +4 -4
- data/lib/metaruby.rb +8 -1
- data/lib/metaruby/class.rb +13 -9
- data/lib/metaruby/dsls.rb +6 -0
- data/lib/metaruby/gui.rb +16 -0
- data/lib/metaruby/gui/exception_rendering.rb +281 -0
- data/lib/metaruby/gui/exception_view.rb +32 -111
- data/lib/metaruby/gui/html.rb +7 -0
- data/lib/metaruby/gui/html/button.rb +54 -12
- data/lib/metaruby/gui/html/collection.rb +33 -9
- data/lib/metaruby/gui/html/exception_view.css +4 -6
- data/lib/metaruby/gui/html/list.rhtml +3 -3
- data/lib/metaruby/gui/html/page.rb +233 -53
- data/lib/metaruby/gui/html/page.rhtml +6 -5
- data/lib/metaruby/gui/model_browser.rb +15 -6
- data/lib/metaruby/gui/model_selector.rb +57 -11
- data/lib/metaruby/gui/rendering_manager.rb +36 -7
- data/lib/metaruby/gui/ruby_constants_item_model.rb +155 -42
- data/lib/metaruby/inherited_attribute.rb +59 -8
- data/lib/metaruby/module.rb +29 -12
- data/lib/metaruby/registration.rb +44 -12
- data/lib/metaruby/test.rb +6 -10
- data/lib/metaruby/version.rb +2 -1
- data/lib/yard-metaruby.rb +10 -3
- data/manifest.xml +1 -0
- data/metaruby.gemspec +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f6d60aa23a3e9e790ac1613c65fc2a0b446f0de
|
4
|
+
data.tar.gz: e22d9104e853727888c91504b5d25dbf29234e9a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e59cba378c250b744c75cd654c426cbbd801812df83e45a07fe2ba22ef15a903490d61d039ea67cdfc275e2dbf629739f1507f30ef16aacd79b52f8948be0c3f
|
7
|
+
data.tar.gz: 58bee189d56019dab2103b3944d3ec24001e044ce16dd76ca3101f9ff097652c44dbdabc6862affeed7f1a522fd7b839c54da0a016236c57447adb1591126de5
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -96,7 +96,7 @@ purpose, that we strongly recommend you use:
|
|
96
96
|
~~~
|
97
97
|
class Module
|
98
98
|
def color(name, &block)
|
99
|
-
MetaRuby::ModelAsModule.
|
99
|
+
MetaRuby::ModelAsModule.create_and_register_submodel(self, name, Color, &block)
|
100
100
|
end
|
101
101
|
end
|
102
102
|
~~~
|
@@ -305,9 +305,9 @@ Car.clear_submodels
|
|
305
305
|
|
306
306
|
This will only clear anonymous models. Models that are created either by
|
307
307
|
subclassing a model class or by using
|
308
|
-
{MetaRuby::ModelAsModule
|
309
|
-
|
310
|
-
{MetaRuby::Registration#permanent_model?
|
308
|
+
{MetaRuby::ModelAsModule.create_and_register_submodel}
|
309
|
+
are marked as
|
310
|
+
{MetaRuby::Registration#permanent_model?} and therefore
|
311
311
|
protected from removal by #clear_submodel
|
312
312
|
|
313
313
|
# Adding options to the submodel creation process
|
data/lib/metaruby.rb
CHANGED
@@ -1,17 +1,24 @@
|
|
1
1
|
require 'utilrb/object/attribute'
|
2
|
+
require 'weakref'
|
2
3
|
|
3
4
|
require 'metaruby/inherited_attribute'
|
4
5
|
require 'metaruby/registration'
|
5
6
|
require 'metaruby/module'
|
6
7
|
require 'metaruby/class'
|
7
8
|
|
9
|
+
require 'utilrb/logger'
|
10
|
+
|
8
11
|
# The toplevel namespace for MetaRuby
|
9
12
|
#
|
10
13
|
# MetaRuby is an implementation of a (very small) modelling toolkit that uses
|
11
14
|
# the Ruby type system as its meta-metamodel
|
12
|
-
require 'utilrb/logger'
|
13
15
|
module MetaRuby
|
16
|
+
# Path to the metaruby.rb file (i.e. the root of the MetaRuby library)
|
17
|
+
#
|
18
|
+
# This is used to find ressources (css, javascript) that is bundled in the
|
19
|
+
# metaruby repository
|
14
20
|
LIB_DIR = File.expand_path('metaruby', File.dirname(__FILE__))
|
21
|
+
|
15
22
|
extend Logger::Root('MetaRuby', Logger::WARN)
|
16
23
|
end
|
17
24
|
|
data/lib/metaruby/class.rb
CHANGED
@@ -28,6 +28,8 @@ module MetaRuby
|
|
28
28
|
# @return [String] set or get the documentation text for this model
|
29
29
|
inherited_single_value_attribute :doc
|
30
30
|
|
31
|
+
# @!attribute [rw] name
|
32
|
+
#
|
31
33
|
# Sets a name on this model
|
32
34
|
#
|
33
35
|
# Only use this on 'anonymous models', i.e. on models that are not
|
@@ -35,6 +37,9 @@ module MetaRuby
|
|
35
37
|
#
|
36
38
|
# @return [String] the assigned name
|
37
39
|
def name=(name)
|
40
|
+
# This is dynamically defined. The reason is that there is no way to
|
41
|
+
# call 'super' to get the default Class#name, so we define our name
|
42
|
+
# only when it is explicitely assigned
|
38
43
|
def self.name
|
39
44
|
if @name then @name
|
40
45
|
else super
|
@@ -69,17 +74,14 @@ module MetaRuby
|
|
69
74
|
# assigned on a Ruby constant
|
70
75
|
#
|
71
76
|
# @return [Module] a subclass of self
|
72
|
-
def new_submodel(
|
73
|
-
options, submodel_options = Kernel.filter_options options,
|
74
|
-
:name => nil
|
75
|
-
|
77
|
+
def new_submodel(name: nil, **submodel_options, &block)
|
76
78
|
Thread.current[FROM_NEW_SUBMODEL_TLS] = true
|
77
79
|
model = self.class.new(self)
|
78
80
|
model.permanent_model = false
|
79
|
-
if
|
80
|
-
model.name =
|
81
|
+
if name
|
82
|
+
model.name = name
|
81
83
|
end
|
82
|
-
setup_submodel(model, submodel_options, &block)
|
84
|
+
setup_submodel(model, **submodel_options, &block)
|
83
85
|
model
|
84
86
|
end
|
85
87
|
|
@@ -89,8 +91,10 @@ module MetaRuby
|
|
89
91
|
end
|
90
92
|
|
91
93
|
# Called at the end of the definition of a new submodel
|
92
|
-
def setup_submodel(submodel,
|
93
|
-
|
94
|
+
def setup_submodel(submodel, register: true, **options, &block)
|
95
|
+
if register
|
96
|
+
register_submodel(submodel)
|
97
|
+
end
|
94
98
|
|
95
99
|
if block_given?
|
96
100
|
submodel.apply_block(&block)
|
data/lib/metaruby/dsls.rb
CHANGED
data/lib/metaruby/gui.rb
CHANGED
@@ -1,9 +1,25 @@
|
|
1
1
|
require 'Qt4'
|
2
2
|
require 'qtwebkit'
|
3
3
|
require 'kramdown'
|
4
|
+
require 'pp'
|
4
5
|
require 'metaruby/gui/html'
|
5
6
|
require 'metaruby/gui/ruby_constants_item_model'
|
6
7
|
require 'metaruby/gui/rendering_manager'
|
7
8
|
require 'metaruby/gui/model_browser'
|
8
9
|
require 'metaruby/gui/model_selector'
|
9
10
|
require 'metaruby/gui/exception_view'
|
11
|
+
|
12
|
+
module MetaRuby
|
13
|
+
# Set of widgets and classes that are used to view/browse MetaRuby-based models using Qt
|
14
|
+
#
|
15
|
+
# Model views are using HTML, the rendering of which is done through
|
16
|
+
# {GUI::HTML::Page}. The main functionality centers around
|
17
|
+
# {GUI::ModelBrowser} which allows to browse models in a hierarchy, and
|
18
|
+
# display them in a HTML::Page.
|
19
|
+
#
|
20
|
+
# {GUI::ExceptionRendering} allows to render exceptions into a HTML page as
|
21
|
+
# well, the formatting being delegated to the exception's #pretty_print
|
22
|
+
# method.
|
23
|
+
module GUI
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,281 @@
|
|
1
|
+
module MetaRuby
|
2
|
+
module GUI
|
3
|
+
# Functionality to render exceptions in an HTML view
|
4
|
+
#
|
5
|
+
# On top of properly formatting the exception, it introduces backtrace
|
6
|
+
# filtering and javascript-based buttons to enable backtraces on or off.
|
7
|
+
#
|
8
|
+
# It is usually not used directly, but through {HTML::Page}
|
9
|
+
#
|
10
|
+
# @see HTML::Page#enable_exception_rendering
|
11
|
+
# @see HTML::Page#push_exception
|
12
|
+
class ExceptionRendering
|
13
|
+
# The directory relative to which ressources (such as css or javascript
|
14
|
+
# files) are resolved by default
|
15
|
+
RESSOURCES_DIR = File.expand_path('html', File.dirname(__FILE__))
|
16
|
+
|
17
|
+
# @return [#link_to] an object that allows to render a link to an
|
18
|
+
# object
|
19
|
+
attr_reader :linker
|
20
|
+
|
21
|
+
# @return [#[]] an object that can be used to determine whether a
|
22
|
+
# file is a user or framework file. It is used in backtrace
|
23
|
+
# filtering and rendering. The default returns true for any file.
|
24
|
+
attr_reader :user_file_filter
|
25
|
+
|
26
|
+
# Sets {#user_file_filter} or resets it to the default
|
27
|
+
#
|
28
|
+
# @param [nil,#[]] filter
|
29
|
+
def user_file_filter=(filter)
|
30
|
+
@user_file_filter = filter || Hash.new(true)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Create an exception rendering object using the given linker object
|
34
|
+
#
|
35
|
+
# @param [#link_to] linker
|
36
|
+
def initialize(linker)
|
37
|
+
@linker = linker
|
38
|
+
self.user_file_filter = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# Necessary header content
|
42
|
+
HEADER = <<-EOD
|
43
|
+
<link rel="stylesheet" href="file://#{File.join(RESSOURCES_DIR, 'exception_view.css')}" type="text/css" />
|
44
|
+
<script type="text/javascript" src="file://#{File.join(RESSOURCES_DIR, 'jquery.min.js')}"></script>
|
45
|
+
EOD
|
46
|
+
|
47
|
+
# The scripts that are used by the other exception templates
|
48
|
+
SCRIPTS = <<-EOD
|
49
|
+
<script type="text/javascript">
|
50
|
+
$(document).ready(function () {
|
51
|
+
$(".backtrace").hide()
|
52
|
+
$("a.backtrace_toggle_filtered").click(function (event) {
|
53
|
+
var eventId = $(this).attr("id");
|
54
|
+
$("#backtrace_full_" + eventId).hide();
|
55
|
+
$("#backtrace_filtered_" + eventId).toggle();
|
56
|
+
event.preventDefault();
|
57
|
+
});
|
58
|
+
$("a.backtrace_toggle_full").click(function (event) {
|
59
|
+
var eventId = $(this).attr("id");
|
60
|
+
$("#backtrace_full_" + eventId).toggle();
|
61
|
+
$("#backtrace_filtered_" + eventId).hide();
|
62
|
+
event.preventDefault();
|
63
|
+
});
|
64
|
+
});
|
65
|
+
</script>
|
66
|
+
EOD
|
67
|
+
|
68
|
+
# Contents necessary in the <head> ... </head> section
|
69
|
+
#
|
70
|
+
# It is used when enabling the renderer on a [Page] by calling
|
71
|
+
# {HTML::Page#add_to_setup}
|
72
|
+
def head
|
73
|
+
HEADER
|
74
|
+
end
|
75
|
+
|
76
|
+
# Scripts block to be added to the HTML document
|
77
|
+
#
|
78
|
+
# It is used when enabling the renderer on a [Page] by calling
|
79
|
+
# {HTML::Page#add_to_setup}
|
80
|
+
def scripts
|
81
|
+
SCRIPTS
|
82
|
+
end
|
83
|
+
|
84
|
+
# Parse a backtrace into its file, line and method consistuents
|
85
|
+
#
|
86
|
+
# @return [Array<(String,Integer,String)>]
|
87
|
+
def self.parse_backtrace(backtrace)
|
88
|
+
BacktraceParser.new(backtrace).parse
|
89
|
+
end
|
90
|
+
|
91
|
+
# Shim class that parses a backtrace into its constituents
|
92
|
+
#
|
93
|
+
# This is an internal class, that should not be used directly. Use
|
94
|
+
# {ExceptionRendering.parse_backtrace} instead.
|
95
|
+
#
|
96
|
+
# It provides the methods required for facet's #call_stack method to
|
97
|
+
# work, thus allowing to use it to parse an arbitrary backtrace
|
98
|
+
class BacktraceParser
|
99
|
+
# Create a parser for the given backtrace
|
100
|
+
def initialize(backtrace)
|
101
|
+
@backtrace = backtrace || []
|
102
|
+
end
|
103
|
+
|
104
|
+
# Parse the backtrace into file, line and method
|
105
|
+
#
|
106
|
+
# @return [Array<(String,Integer,String)>]
|
107
|
+
def parse
|
108
|
+
call_stack(0)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns the backtrace
|
112
|
+
#
|
113
|
+
# This is required by facet's #call_stack
|
114
|
+
def pp_callstack(level)
|
115
|
+
@backtrace[level..-1]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns the backtrace
|
119
|
+
#
|
120
|
+
# This is required by facet's #call_stack
|
121
|
+
def pp_call_stack(level)
|
122
|
+
@backtrace[level..-1]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Template used to render an exception that does not have backtrace
|
127
|
+
EXCEPTION_TEMPLATE_WITHOUT_BACKTRACE = <<-EOF
|
128
|
+
<div class="message" id="<%= id %>"><pre><%= message.join("\n") %></pre></div>
|
129
|
+
EOF
|
130
|
+
|
131
|
+
# Template used to render an exception that does have a backtrace
|
132
|
+
EXCEPTION_TEMPLATE_WITH_BACKTRACE = <<-EOF
|
133
|
+
<div class="message" id="<%= id %>">
|
134
|
+
<pre><%= message.join("\n") %></pre>
|
135
|
+
<span class="backtrace_links">
|
136
|
+
(show: <a class="backtrace_toggle_filtered" id="<%= id %>">filtered backtrace</a>,
|
137
|
+
<a class=\"backtrace_toggle_full\" id="<%= id %>">full backtrace</a>)
|
138
|
+
</span>
|
139
|
+
</div>
|
140
|
+
<div class="backtrace_summary">
|
141
|
+
from <%= origin_file %>:<%= origin_line %>:in <%= HTML.escape_html(origin_method.to_s) %>
|
142
|
+
</div>
|
143
|
+
<div class="backtrace" id="backtrace_filtered_<%= id %>">
|
144
|
+
<%= render_backtrace(filtered_backtrace) %>
|
145
|
+
</div>
|
146
|
+
<div class="backtrace" id="backtrace_full_<%= id %>">
|
147
|
+
<%= render_backtrace(full_backtrace) %>
|
148
|
+
</div>
|
149
|
+
EOF
|
150
|
+
|
151
|
+
# Filters the backtrace to remove framework parts that are not
|
152
|
+
# relevant
|
153
|
+
#
|
154
|
+
# @param [Array<(String,Integer,Symbol)>] parsed_backtrace the parsed backtrace
|
155
|
+
# @param [Array<(String,Integer,Symbol)>] raw_backtrace the raw backtrace
|
156
|
+
def filter_backtrace(parsed_backtrace, raw_backtrace)
|
157
|
+
head = parsed_backtrace.take_while { |file, _| !user_file?(file) }
|
158
|
+
tail = parsed_backtrace[head.size..-1].find_all { |file, _| user_file?(file) }
|
159
|
+
head + tail
|
160
|
+
end
|
161
|
+
|
162
|
+
# Return true if the given file is a user file or a framework file
|
163
|
+
#
|
164
|
+
# An object used to determine this can be set with
|
165
|
+
# {#user_file_filter=}
|
166
|
+
#
|
167
|
+
# This is used by {#render_backtrace} to choose the style of a
|
168
|
+
# backtrace line
|
169
|
+
def user_file?(file)
|
170
|
+
user_file_filter[file]
|
171
|
+
end
|
172
|
+
|
173
|
+
@@exception_id = 0
|
174
|
+
|
175
|
+
# Automatically generate an exception ID
|
176
|
+
def allocate_exception_id
|
177
|
+
@@exception_id += 1
|
178
|
+
end
|
179
|
+
|
180
|
+
# Render an exception into HTML
|
181
|
+
#
|
182
|
+
# @param [Exception] e the exception to be rendered
|
183
|
+
# @param [String] reason additional string that describes the
|
184
|
+
# exception reason
|
185
|
+
# @param [String] id the ID that should be used to identify the
|
186
|
+
# exception. Since a given exception can "contain" more than one
|
187
|
+
# (see {#each_exception_from}), a -#counter pattern is added to
|
188
|
+
# the ID.
|
189
|
+
# @return [String]
|
190
|
+
def render(e, reason = nil, id = allocate_exception_id)
|
191
|
+
counter = 0
|
192
|
+
html = []
|
193
|
+
seen = Set.new
|
194
|
+
each_exception_from(e) do |exception|
|
195
|
+
if !seen.include?(exception)
|
196
|
+
seen << exception
|
197
|
+
html << render_single_exception(e, "#{id}-#{counter += 1}")
|
198
|
+
end
|
199
|
+
end
|
200
|
+
html.join("\n")
|
201
|
+
end
|
202
|
+
|
203
|
+
# Method used by {#render} to discover all exception objects that
|
204
|
+
# are linked to another exception, in cases where exceptions cause
|
205
|
+
# one another
|
206
|
+
#
|
207
|
+
# The default implementation only yields 'e', reimplement in
|
208
|
+
# subclasses
|
209
|
+
#
|
210
|
+
# @yieldparam [Exception] exception an exception
|
211
|
+
def each_exception_from(e)
|
212
|
+
return enum_for(__method__) if !block_given?
|
213
|
+
yield(e)
|
214
|
+
end
|
215
|
+
|
216
|
+
# @api private
|
217
|
+
#
|
218
|
+
# Parses the exception backtrace, and generate a parsed raw and
|
219
|
+
# parsed filtered version of it
|
220
|
+
#
|
221
|
+
# @return [(Array<(String,Integer,String)>,Array<(String,Integer,String))>
|
222
|
+
# the full and filtered backtraces, as list of tuples
|
223
|
+
# (file,line,method)
|
224
|
+
def parse_and_filter_backtrace(backtrace)
|
225
|
+
full_backtrace = ExceptionRendering.parse_backtrace(backtrace)
|
226
|
+
filtered_backtrace = filter_backtrace(full_backtrace, backtrace)
|
227
|
+
if filtered_backtrace.first.respond_to?(:to_str)
|
228
|
+
filtered_backtrace = ExceptionRendering.parse_backtrace(filtered_backtrace)
|
229
|
+
end
|
230
|
+
return full_backtrace, filtered_backtrace
|
231
|
+
end
|
232
|
+
|
233
|
+
# @api private
|
234
|
+
#
|
235
|
+
# Render a single exception object into a HTML block
|
236
|
+
#
|
237
|
+
# @param [Exception] e the exception
|
238
|
+
# @param [String] id the block ID
|
239
|
+
# @return [String]
|
240
|
+
def render_single_exception(e, id)
|
241
|
+
message = PP.pp(e, "").split("\n").
|
242
|
+
map { |line| HTML.escape_html(line) }
|
243
|
+
|
244
|
+
full_backtrace, filtered_backtrace =
|
245
|
+
parse_and_filter_backtrace(e.backtrace || Array.new)
|
246
|
+
|
247
|
+
if !full_backtrace.empty?
|
248
|
+
origin_file, origin_line, origin_method =
|
249
|
+
filtered_backtrace.find { |file, _| user_file?(file) } ||
|
250
|
+
filtered_backtrace.first ||
|
251
|
+
full_backtrace.first
|
252
|
+
|
253
|
+
origin_file = linker.link_to(Pathname.new(origin_file), origin_file, lineno: origin_line)
|
254
|
+
ERB.new(EXCEPTION_TEMPLATE_WITH_BACKTRACE).result(binding)
|
255
|
+
else
|
256
|
+
ERB.new(EXCEPTION_TEMPLATE_WITHOUT_BACKTRACE).result(binding)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# @api private
|
261
|
+
#
|
262
|
+
# Render a backtrace
|
263
|
+
#
|
264
|
+
# It uses {#linker} to generate links, and {#user_file?} to change
|
265
|
+
# the style of the backtrace line.
|
266
|
+
def render_backtrace(backtrace)
|
267
|
+
result = []
|
268
|
+
backtrace.each do |file, line, method|
|
269
|
+
file_link = linker.link_to(Pathname.new(file), file, lineno: line)
|
270
|
+
if user_file?(file)
|
271
|
+
result << " <span class=\"user_file\">#{file_link}:#{line}:in #{HTML.escape_html(method.to_s)}</span><br/>"
|
272
|
+
else
|
273
|
+
result << " #{file_link}:#{line}:in #{HTML.escape_html(method.to_s)}<br/>"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
result.join("\n")
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|