metaruby 1.0.0.rc2 → 1.0.0.rc3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|