glimmer-dsl-swt 4.22.2.1 → 4.22.2.5

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/README.md +26 -12
  4. data/VERSION +1 -1
  5. data/docs/reference/GLIMMER_COMMAND.md +3 -1
  6. data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +145 -0
  7. data/docs/reference/GLIMMER_SAMPLES.md +17 -9
  8. data/glimmer-dsl-swt.gemspec +0 -0
  9. data/lib/glimmer/dsl/swt/exec_expression.rb +1 -2
  10. data/lib/glimmer/launcher.rb +7 -4
  11. data/lib/glimmer/rake_task/scaffold.rb +28 -15
  12. data/lib/glimmer/swt/image_proxy.rb +1 -0
  13. data/lib/glimmer/swt/layout_data_proxy.rb +4 -0
  14. data/samples/elaborate/calculator.rb +12 -12
  15. data/samples/elaborate/contact_manager.rb +26 -14
  16. data/samples/elaborate/game_of_life.rb +8 -8
  17. data/samples/elaborate/klondike_solitaire/view/action_panel.rb +2 -2
  18. data/samples/elaborate/klondike_solitaire/view/klondike_solitaire_menu_bar.rb +15 -10
  19. data/samples/elaborate/login.rb +16 -10
  20. data/samples/elaborate/mandelbrot_fractal.rb +26 -20
  21. data/samples/elaborate/meta_sample.rb +6 -6
  22. data/samples/elaborate/metronome.rb +4 -4
  23. data/samples/elaborate/snake/model/apple.rb +33 -0
  24. data/samples/elaborate/snake/model/game.rb +68 -0
  25. data/samples/elaborate/snake/model/snake.rb +95 -0
  26. data/samples/elaborate/snake/model/vertebra.rb +22 -0
  27. data/samples/elaborate/snake/presenter/cell.rb +27 -0
  28. data/samples/elaborate/snake/presenter/grid.rb +49 -0
  29. data/samples/elaborate/snake.rb +103 -0
  30. data/samples/elaborate/stock_ticker.rb +12 -12
  31. data/samples/elaborate/tetris.rb +10 -10
  32. data/samples/elaborate/timer.rb +36 -26
  33. data/samples/elaborate/user_profile.rb +53 -14
  34. data/samples/elaborate/weather.rb +2 -2
  35. data/samples/hello/hello_button.rb +3 -3
  36. data/samples/hello/hello_canvas_animation.rb +2 -2
  37. data/samples/hello/hello_color_dialog.rb +4 -4
  38. data/samples/hello/hello_custom_shell.rb +2 -2
  39. data/samples/hello/hello_dialog.rb +4 -4
  40. data/samples/hello/hello_directory_dialog.rb +2 -2
  41. data/samples/hello/hello_expand_bar.rb +4 -5
  42. data/samples/hello/hello_file_dialog.rb +2 -2
  43. data/samples/hello/hello_font_dialog.rb +2 -2
  44. data/samples/hello/hello_link.rb +5 -4
  45. data/samples/hello/hello_list_multi_selection.rb +3 -1
  46. data/samples/hello/hello_list_single_selection.rb +3 -1
  47. data/samples/hello/hello_menu_bar.rb +28 -28
  48. data/samples/hello/hello_message_box.rb +2 -2
  49. data/samples/hello/hello_pop_up_context_menu.rb +18 -8
  50. data/samples/hello/hello_shell.rb +16 -16
  51. data/samples/hello/hello_styled_text.rb +2 -2
  52. data/samples/hello/hello_table.rb +4 -4
  53. data/samples/hello/hello_tray_item.rb +2 -2
  54. metadata +9 -2
@@ -40,6 +40,7 @@ class ContactManager
40
40
  margin_width 0
41
41
  margin_height 0
42
42
  }
43
+
43
44
  layout_data :fill, :center, true, false
44
45
  text 'Lookup Contacts'
45
46
  font height: 24
@@ -52,9 +53,10 @@ class ContactManager
52
53
  text {
53
54
  layout_data :fill, :center, true, false
54
55
  text <=> [@contact_manager_presenter, :first_name]
55
- on_key_pressed {|key_event|
56
+
57
+ on_key_pressed do |key_event|
56
58
  @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
57
- }
59
+ end
58
60
  }
59
61
 
60
62
  label {
@@ -65,9 +67,10 @@ class ContactManager
65
67
  text {
66
68
  layout_data :fill, :center, true, false
67
69
  text <=> [@contact_manager_presenter, :last_name]
68
- on_key_pressed {|key_event|
70
+
71
+ on_key_pressed do |key_event|
69
72
  @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
70
- }
73
+ end
71
74
  }
72
75
 
73
76
  label {
@@ -78,9 +81,10 @@ class ContactManager
78
81
  text {
79
82
  layout_data :fill, :center, true, false
80
83
  text <=> [@contact_manager_presenter, :email]
81
- on_key_pressed {|key_event|
84
+
85
+ on_key_pressed do |key_event|
82
86
  @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
83
- }
87
+ end
84
88
  }
85
89
 
86
90
  composite {
@@ -94,18 +98,26 @@ class ContactManager
94
98
 
95
99
  button {
96
100
  text "&Find"
97
- on_widget_selected { @contact_manager_presenter.find }
98
- on_key_pressed {|key_event|
101
+
102
+ on_widget_selected do
103
+ @contact_manager_presenter.find
104
+ end
105
+
106
+ on_key_pressed do |key_event|
99
107
  @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
100
- }
108
+ end
101
109
  }
102
110
 
103
111
  button {
104
112
  text "&List All"
105
- on_widget_selected { @contact_manager_presenter.list }
106
- on_key_pressed {|key_event|
113
+
114
+ on_widget_selected do
115
+ @contact_manager_presenter.list
116
+ end
117
+
118
+ on_key_pressed do |key_event|
107
119
  @contact_manager_presenter.list if key_event.keyCode == swt(:cr)
108
- }
120
+ end
109
121
  }
110
122
  }
111
123
  }
@@ -134,9 +146,9 @@ class ContactManager
134
146
 
135
147
  items <=> [@contact_manager_presenter, :results, column_properties: [:first_name, :last_name, :email]]
136
148
 
137
- on_mouse_up { |event|
149
+ on_mouse_up do |event|
138
150
  table_proxy.edit_table_item(event.table_item, event.column_index)
139
- }
151
+ end
140
152
  }
141
153
  }
142
154
  }
@@ -50,9 +50,9 @@ class GameOfLife
50
50
  rectangle(column_index*CELL_WIDTH, row_index*CELL_HEIGHT, CELL_WIDTH, CELL_HEIGHT) {
51
51
  background <= [@grid.cell_rows[row_index][column_index], "alive", on_read: ->(a) {a ? :black : :white}]
52
52
 
53
- on_mouse_down {
53
+ on_mouse_down do
54
54
  @grid.cell_rows[row_index][column_index].toggle_aliveness!
55
- }
55
+ end
56
56
  }
57
57
  end
58
58
  end
@@ -67,26 +67,26 @@ class GameOfLife
67
67
  text 'Step'
68
68
  enabled <= [@grid, :playing, on_read: :! ]
69
69
 
70
- on_widget_selected {
70
+ on_widget_selected do
71
71
  @grid.step!
72
- }
72
+ end
73
73
  }
74
74
 
75
75
  button {
76
76
  text 'Clear'
77
77
  enabled <= [@grid, :playing, on_read: :! ]
78
78
 
79
- on_widget_selected {
79
+ on_widget_selected do
80
80
  @grid.clear!
81
- }
81
+ end
82
82
  }
83
83
 
84
84
  button {
85
85
  text <= [@grid, :playing, on_read: ->(p) { p ? 'Stop' : 'Play' }]
86
86
 
87
- on_widget_selected {
87
+ on_widget_selected do
88
88
  @grid.toggle_playback!
89
- }
89
+ end
90
90
  }
91
91
 
92
92
  label {
@@ -19,9 +19,9 @@ class KlondikeSolitaire
19
19
 
20
20
  text 'Restart Game'
21
21
 
22
- on_widget_selected {
22
+ on_widget_selected do
23
23
  game.restart!
24
- }
24
+ end
25
25
  }
26
26
  }
27
27
  }
@@ -7,12 +7,13 @@ class KlondikeSolitaire
7
7
 
8
8
  before_body do
9
9
  @display = display {
10
- on_about {
10
+ on_about do
11
11
  display_about_dialog
12
- }
13
- on_preferences {
12
+ end
13
+
14
+ on_preferences do
14
15
  display_about_dialog
15
- }
16
+ end
16
17
  }
17
18
  end
18
19
 
@@ -20,30 +21,34 @@ class KlondikeSolitaire
20
21
  menu_bar {
21
22
  menu {
22
23
  text '&Game'
24
+
23
25
  menu_item {
24
26
  text '&Restart'
25
27
  accelerator (OS.mac? ? :command : :ctrl), :r
26
28
 
27
- on_widget_selected {
29
+ on_widget_selected do
28
30
  game.restart!
29
- }
31
+ end
30
32
  }
33
+
31
34
  menu_item {
32
35
  text 'E&xit'
33
36
  accelerator :alt, :f4
34
37
 
35
- on_widget_selected {
38
+ on_widget_selected do
36
39
  exit(0)
37
- }
40
+ end
38
41
  }
39
42
  }
40
43
  menu {
41
44
  text '&Help'
45
+
42
46
  menu_item {
43
47
  text '&About...'
44
- on_widget_selected {
48
+
49
+ on_widget_selected do
45
50
  display_about_dialog
46
- }
51
+ end
47
52
  }
48
53
  }
49
54
  }
@@ -81,9 +81,9 @@ class Login
81
81
  text <=> [@presenter, :user_name]
82
82
  enabled <= [@presenter, :logged_out?, computed_by: :status]
83
83
 
84
- on_key_pressed { |event|
84
+ on_key_pressed do |event|
85
85
  @password_text.set_focus if event.keyCode == swt(:cr)
86
- }
86
+ end
87
87
  }
88
88
 
89
89
  label { text "Password:" }
@@ -91,9 +91,9 @@ class Login
91
91
  text <=> [@presenter, :password]
92
92
  enabled <= [@presenter, :logged_out?, computed_by: :status]
93
93
 
94
- on_key_pressed { |event|
94
+ on_key_pressed do |event|
95
95
  @presenter.login! if event.keyCode == swt(:cr)
96
- }
96
+ end
97
97
  }
98
98
 
99
99
  label { text "Status:" }
@@ -103,25 +103,31 @@ class Login
103
103
  text "Login"
104
104
  enabled <= [@presenter, :logged_out?, computed_by: :status]
105
105
 
106
- on_widget_selected { @presenter.login! }
107
- on_key_pressed { |event|
106
+ on_widget_selected do
107
+ @presenter.login!
108
+ end
109
+
110
+ on_key_pressed do |event|
108
111
  if event.keyCode == swt(:cr)
109
112
  @presenter.login!
110
113
  end
111
- }
114
+ end
112
115
  }
113
116
 
114
117
  button {
115
118
  text "Logout"
116
119
  enabled <= [@presenter, :logged_in?, computed_by: :status]
117
120
 
118
- on_widget_selected { @presenter.logout! }
119
- on_key_pressed { |event|
121
+ on_widget_selected do
122
+ @presenter.logout!
123
+ end
124
+
125
+ on_key_pressed do |event|
120
126
  if event.keyCode == swt(:cr)
121
127
  @presenter.logout!
122
128
  @user_name_text.set_focus
123
129
  end
124
- }
130
+ end
125
131
  }
126
132
  }
127
133
  }
@@ -186,10 +186,10 @@ class MandelbrotFractal
186
186
  minimum_size mandelbrot.width + 29, mandelbrot.height + 77
187
187
  image @mandelbrot_image
188
188
 
189
- on_shell_closed {
189
+ on_shell_closed do
190
190
  @thread.kill # should not be dangerous in this case
191
191
  puts "Mandelbrot background calculation stopped!"
192
- }
192
+ end
193
193
 
194
194
  progress_bar {
195
195
  layout_data :fill, :center, true, false
@@ -205,27 +205,27 @@ class MandelbrotFractal
205
205
  image @mandelbrot_image
206
206
  cursor :no
207
207
 
208
- on_mouse_down {
208
+ on_mouse_down do
209
209
  @drag_detected = false
210
210
  @canvas.cursor = :hand
211
- }
211
+ end
212
212
 
213
- on_drag_detected { |drag_detect_event|
213
+ on_drag_detected do |drag_detect_event|
214
214
  @drag_detected = true
215
215
  @drag_start_x = drag_detect_event.x
216
216
  @drag_start_y = drag_detect_event.y
217
- }
217
+ end
218
218
 
219
- on_mouse_move { |mouse_event|
219
+ on_mouse_move do |mouse_event|
220
220
  if @drag_detected
221
221
  origin = @scrolled_composite.origin
222
222
  new_x = origin.x - (mouse_event.x - @drag_start_x)
223
223
  new_y = origin.y - (mouse_event.y - @drag_start_y)
224
224
  @scrolled_composite.set_origin(new_x, new_y)
225
225
  end
226
- }
226
+ end
227
227
 
228
- on_mouse_up { |mouse_event|
228
+ on_mouse_up do |mouse_event|
229
229
  if !@drag_detected
230
230
  origin = @scrolled_composite.origin
231
231
  @location_x = mouse_event.x
@@ -238,7 +238,7 @@ class MandelbrotFractal
238
238
  end
239
239
  @canvas.cursor = can_zoom_in? ? :cross : :no
240
240
  @drag_detected = false
241
- }
241
+ end
242
242
 
243
243
  }
244
244
  }
@@ -251,27 +251,33 @@ class MandelbrotFractal
251
251
  text 'Zoom &In'
252
252
  accelerator COMMAND, '+'
253
253
 
254
- on_widget_selected { zoom_in }
254
+ on_widget_selected do
255
+ zoom_in
256
+ end
255
257
  }
256
258
 
257
259
  menu_item {
258
260
  text 'Zoom &Out'
259
261
  accelerator COMMAND, '-'
260
262
 
261
- on_widget_selected { zoom_out }
263
+ on_widget_selected do
264
+ zoom_out
265
+ end
262
266
  }
263
267
 
264
268
  menu_item {
265
269
  text '&Reset Zoom'
266
270
  accelerator COMMAND, '0'
267
271
 
268
- on_widget_selected { perform_zoom(mandelbrot_zoom: 1.0) }
272
+ on_widget_selected do
273
+ perform_zoom(mandelbrot_zoom: 1.0)
274
+ end
269
275
  }
270
276
  }
271
277
  menu {
272
278
  text '&Cores'
273
279
 
274
- Concurrent.physical_processor_count.times {|n|
280
+ Concurrent.physical_processor_count.times do |n|
275
281
  processor_number = n + 1
276
282
  menu_item(:radio) {
277
283
  text "&#{processor_number}"
@@ -287,11 +293,11 @@ class MandelbrotFractal
287
293
 
288
294
  selection true if processor_number == Concurrent.physical_processor_count
289
295
 
290
- on_widget_selected {
296
+ on_widget_selected do
291
297
  Mandelbrot.processor_count = processor_number
292
- }
298
+ end
293
299
  }
294
- }
300
+ end
295
301
  }
296
302
  menu {
297
303
  text '&Help'
@@ -300,9 +306,9 @@ class MandelbrotFractal
300
306
  text '&Instructions'
301
307
  accelerator COMMAND, :shift, :i
302
308
 
303
- on_widget_selected {
309
+ on_widget_selected do
304
310
  display_help_instructions
305
- }
311
+ end
306
312
  }
307
313
  }
308
314
  }
@@ -352,7 +358,7 @@ class MandelbrotFractal
352
358
  def color_palette
353
359
  if @color_palette.nil?
354
360
  @color_palette = [[0, 0, 0]] + 40.times.map { |i| [255 - i*5, 255 - i*5, 55 + i*5] }
355
- @color_palette = @color_palette.map {|color_data| rgb(*color_data).swt_color}
361
+ @color_palette = @color_palette.map { |color_data| rgb(*color_data).swt_color }
356
362
  end
357
363
  @color_palette
358
364
  end
@@ -253,22 +253,22 @@ class MetaSampleApplication
253
253
  font height: 25
254
254
  enabled <= [SampleDirectory, 'selected_sample.launchable']
255
255
 
256
- on_widget_selected {
256
+ on_widget_selected do
257
257
  begin
258
258
  SampleDirectory.selected_sample.launch(@code_text.text)
259
259
  rescue LoadError, StandardError, SyntaxError => launch_error
260
260
  error_dialog(message: launch_error.full_message).open
261
261
  end
262
- }
262
+ end
263
263
  }
264
264
  button {
265
265
  text 'Reset'
266
266
  font height: 25
267
267
  enabled <= [SampleDirectory, 'selected_sample.editable']
268
268
 
269
- on_widget_selected {
269
+ on_widget_selected do
270
270
  SampleDirectory.selected_sample.reset_code!
271
- }
271
+ end
272
272
  }
273
273
  }
274
274
  }
@@ -319,9 +319,9 @@ class MetaSampleApplication
319
319
  button {
320
320
  text 'Close'
321
321
 
322
- on_widget_selected {
322
+ on_widget_selected do
323
323
  dialog_proxy.close
324
- }
324
+ end
325
325
  }
326
326
  }
327
327
  end
@@ -101,13 +101,13 @@ class Metronome
101
101
 
102
102
  @beat_container = beat_container
103
103
 
104
- on_swt_show {
104
+ on_swt_show do
105
105
  start_metronome
106
- }
106
+ end
107
107
 
108
- on_widget_disposed {
108
+ on_widget_disposed do
109
109
  stop_metronome
110
- }
110
+ end
111
111
  }
112
112
  }
113
113
 
@@ -0,0 +1,33 @@
1
+ class Snake
2
+ module Model
3
+ class Apple
4
+ attr_reader :game
5
+ attr_accessor :row, :column
6
+
7
+ def initialize(game)
8
+ @game = game
9
+ end
10
+
11
+ # generates a new location from scratch or via dependency injection of what cell is (for testing purposes)
12
+ def generate(initial_row: nil, initial_column: nil)
13
+ if initial_row && initial_column
14
+ self.row, self.column = initial_row, initial_column
15
+ else
16
+ self.row, self.column = @game.height.times.zip(@game.width.times).reject do |row, column|
17
+ @game.snake.vertebrae.map {|v| [v.row, v.column]}.include?([row, column])
18
+ end.sample
19
+ end
20
+ end
21
+
22
+ def remove
23
+ self.row = nil
24
+ self.column = nil
25
+ end
26
+
27
+ # inspect is overridden to prevent printing very long stack traces
28
+ def inspect
29
+ "#{super[0, 120]}... >"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,68 @@
1
+ require 'fileutils'
2
+
3
+ require_relative 'snake'
4
+ require_relative 'apple'
5
+
6
+ class Snake
7
+ module Model
8
+ class Game
9
+ WIDTH_DEFAULT = 20
10
+ HEIGHT_DEFAULT = 20
11
+ FILE_HIGH_SCORE = File.expand_path(File.join(Dir.home, '.glimmer-snake'))
12
+
13
+ attr_reader :width, :height
14
+ attr_accessor :snake, :apple, :over, :score, :high_score, :paused
15
+ alias over? over
16
+ alias paused? paused
17
+
18
+ def initialize(width = WIDTH_DEFAULT, height = HEIGHT_DEFAULT)
19
+ @width = width
20
+ @height = height
21
+ @snake = Snake.new(self)
22
+ @apple = Apple.new(self)
23
+ FileUtils.touch(FILE_HIGH_SCORE)
24
+ @high_score = File.read(FILE_HIGH_SCORE).to_i rescue 0
25
+ end
26
+
27
+ def score=(new_score)
28
+ @score = new_score
29
+ self.high_score = @score if @score > @high_score
30
+ end
31
+
32
+ def high_score=(new_high_score)
33
+ @high_score = new_high_score
34
+ File.write(FILE_HIGH_SCORE, @high_score.to_s)
35
+ rescue => e
36
+ puts e.full_message
37
+ end
38
+
39
+ def start
40
+ self.over = false
41
+ self.score = 0
42
+ self.snake.generate
43
+ self.apple.generate
44
+ end
45
+
46
+ def pause
47
+ self.paused = true
48
+ end
49
+
50
+ def resume
51
+ self.paused = false
52
+ end
53
+
54
+ def toggle_pause
55
+ unless paused?
56
+ pause
57
+ else
58
+ resume
59
+ end
60
+ end
61
+
62
+ # inspect is overridden to prevent printing very long stack traces
63
+ def inspect
64
+ "#{super[0, 75]}... >"
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,95 @@
1
+ require_relative 'vertebra'
2
+
3
+ class Snake
4
+ module Model
5
+ class Snake
6
+ SCORE_EAT_APPLE = 50
7
+ RIGHT_TURN_MAP = {
8
+ north: :east,
9
+ east: :south,
10
+ south: :west,
11
+ west: :north
12
+ }
13
+ LEFT_TURN_MAP = RIGHT_TURN_MAP.invert
14
+
15
+ attr_accessor :collided
16
+ alias collided? collided
17
+
18
+ attr_reader :game
19
+ # vertebrae and joins are ordered from tail to head
20
+ attr_accessor :vertebrae
21
+
22
+ def initialize(game)
23
+ @game = game
24
+ end
25
+
26
+ # generates a new snake location and orientation from scratch or via dependency injection of what head_cell and orientation are (for testing purposes)
27
+ def generate(initial_row: nil, initial_column: nil, initial_orientation: nil)
28
+ self.collided = false
29
+ initial_vertebra = Vertebra.new(snake: self, row: initial_row, column: initial_column, orientation: initial_orientation)
30
+ self.vertebrae = [initial_vertebra]
31
+ end
32
+
33
+ def length
34
+ @vertebrae.length
35
+ end
36
+
37
+ def head
38
+ @vertebrae.last
39
+ end
40
+
41
+ def tail
42
+ @vertebrae.first
43
+ end
44
+
45
+ def remove
46
+ self.vertebrae.clear
47
+ self.joins.clear
48
+ end
49
+
50
+ def move
51
+ @old_tail = tail.dup
52
+ @new_head = head.dup
53
+ case @new_head.orientation
54
+ when :east
55
+ @new_head.column = (@new_head.column + 1) % @game.width
56
+ when :west
57
+ @new_head.column = (@new_head.column - 1) % @game.width
58
+ when :south
59
+ @new_head.row = (@new_head.row + 1) % @game.height
60
+ when :north
61
+ @new_head.row = (@new_head.row - 1) % @game.height
62
+ end
63
+ if @vertebrae.map {|v| [v.row, v.column]}.include?([@new_head.row, @new_head.column])
64
+ self.collided = true
65
+ @game.over = true
66
+ else
67
+ @vertebrae.append(@new_head)
68
+ @vertebrae.delete(tail)
69
+ if head.row == @game.apple.row && head.column == @game.apple.column
70
+ grow
71
+ @game.apple.generate
72
+ end
73
+ end
74
+ end
75
+
76
+ def turn_right
77
+ head.orientation = RIGHT_TURN_MAP[head.orientation]
78
+ end
79
+
80
+ def turn_left
81
+ head.orientation = LEFT_TURN_MAP[head.orientation]
82
+ end
83
+
84
+ def grow
85
+ @game.score += SCORE_EAT_APPLE
86
+ @vertebrae.prepend(@old_tail)
87
+ end
88
+
89
+ # inspect is overridden to prevent printing very long stack traces
90
+ def inspect
91
+ "#{super[0, 150]}... >"
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,22 @@
1
+ class Snake
2
+ module Model
3
+ class Vertebra
4
+ ORIENTATIONS = %i[north east south west]
5
+ # orientation is needed for snake occuppied cells (but not apple cells)
6
+ attr_reader :snake
7
+ attr_accessor :row, :column, :orientation
8
+
9
+ def initialize(snake: , row: , column: , orientation: )
10
+ @row = row || rand(snake.game.height)
11
+ @column = column || rand(snake.game.width)
12
+ @orientation = orientation || ORIENTATIONS.sample
13
+ @snake = snake
14
+ end
15
+
16
+ # inspect is overridden to prevent printing very long stack traces
17
+ def inspect
18
+ "#{super[0, 150]}... >"
19
+ end
20
+ end
21
+ end
22
+ end