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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: edefd7e59dcbf75b2cd69877ae482a9cb6838b96
4
- data.tar.gz: 4d1263ce781eec239a82dbf30c470e95b9e5030d
3
+ metadata.gz: 0f6d60aa23a3e9e790ac1613c65fc2a0b446f0de
4
+ data.tar.gz: e22d9104e853727888c91504b5d25dbf29234e9a
5
5
  SHA512:
6
- metadata.gz: 2e687ba3e7144b5f21f6ce767ed38364b9baf863ca8bc0b03ee353fbc159265f79569e1f8c7819365f78443e4a2650b40bdb96ae2e2d6653e2b23c6e5bf0643e
7
- data.tar.gz: 7da81dcac85b7e7546734d5eafebe64fa280d59fa5eb579d1ee15b71b2282db7c7aeb09d6c53fa0d018dd2b0368b79a061a30d449b77ada89b9f5ed7ff4e0e95
6
+ metadata.gz: e59cba378c250b744c75cd654c426cbbd801812df83e45a07fe2ba22ef15a903490d61d039ea67cdfc275e2dbf629739f1507f30ef16aacd79b52f8948be0c3f
7
+ data.tar.gz: 58bee189d56019dab2103b3944d3ec24001e044ce16dd76ca3101f9ff097652c44dbdabc6862affeed7f1a522fd7b839c54da0a016236c57447adb1591126de5
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  .yardoc
2
2
  coverage/
3
3
  doc/
4
+ .*.sw?
data/.travis.yml CHANGED
@@ -1,5 +1,9 @@
1
+ sudo: false
1
2
  language: ruby
2
3
  rvm:
3
4
  - 2.0.0
4
5
  - 2.1.6
5
6
  - 2.2.2
7
+ script:
8
+ - bundle exec rake
9
+ - bundle exec rake test
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.create_ang_register_submodel(self, name, Color, &block)
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#create_ang_register_submodel
309
- create_ang_register_submodel} are marked as
310
- {MetaRuby::Registration#permanent_model? permanent models} and therefore
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
 
@@ -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(options = Hash.new, &block)
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 options[:name]
80
- model.name = options[: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, options = Hash.new, &block)
93
- register_submodel(submodel)
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
@@ -1,2 +1,8 @@
1
1
  require 'metaruby/dsls/doc'
2
2
  require 'metaruby/dsls/find_through_method_missing'
3
+
4
+ module MetaRuby
5
+ # Helper functions to build DSLs
6
+ module DSLs
7
+ end
8
+ end
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
+