fidgit 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/.gitignore +7 -7
  2. data/.rspec +2 -2
  3. data/CHANGELOG.md +30 -30
  4. data/Gemfile +3 -3
  5. data/LICENSE.txt +19 -19
  6. data/README.textile +139 -139
  7. data/Rakefile +37 -37
  8. data/config/default_schema.yml +216 -216
  9. data/examples/_all_examples.rb +9 -9
  10. data/examples/align_example.rb +55 -55
  11. data/examples/button_and_toggle_button_example.rb +37 -37
  12. data/examples/color_picker_example.rb +16 -16
  13. data/examples/color_well_example.rb +24 -24
  14. data/examples/combo_box_example.rb +23 -23
  15. data/examples/file_dialog_example.rb +41 -41
  16. data/examples/grid_packer_example.rb +28 -28
  17. data/examples/helpers/example_window.rb +16 -16
  18. data/examples/label_example.rb +22 -22
  19. data/examples/list_example.rb +22 -22
  20. data/examples/menu_pane_example.rb +26 -26
  21. data/examples/message_dialog_example.rb +64 -64
  22. data/examples/radio_button_example.rb +36 -36
  23. data/examples/readme_example.rb +31 -31
  24. data/examples/scroll_window_example.rb +48 -48
  25. data/examples/slider_example.rb +33 -33
  26. data/examples/splash_example.rb +41 -41
  27. data/examples/text_area_example.rb +32 -32
  28. data/fidgit.gemspec +35 -35
  29. data/lib/fidgit.rb +50 -50
  30. data/lib/fidgit/chingu_ext/window.rb +5 -5
  31. data/lib/fidgit/cursor.rb +37 -37
  32. data/lib/fidgit/elements/button.rb +112 -112
  33. data/lib/fidgit/elements/color_picker.rb +62 -62
  34. data/lib/fidgit/elements/color_well.rb +38 -38
  35. data/lib/fidgit/elements/combo_box.rb +113 -113
  36. data/lib/fidgit/elements/composite.rb +16 -16
  37. data/lib/fidgit/elements/container.rb +208 -208
  38. data/lib/fidgit/elements/element.rb +297 -297
  39. data/lib/fidgit/elements/file_browser.rb +151 -151
  40. data/lib/fidgit/elements/grid.rb +226 -226
  41. data/lib/fidgit/elements/group.rb +64 -64
  42. data/lib/fidgit/elements/horizontal.rb +11 -11
  43. data/lib/fidgit/elements/image_frame.rb +64 -64
  44. data/lib/fidgit/elements/label.rb +84 -84
  45. data/lib/fidgit/elements/list.rb +46 -46
  46. data/lib/fidgit/elements/main_packer.rb +24 -24
  47. data/lib/fidgit/elements/menu_pane.rb +160 -160
  48. data/lib/fidgit/elements/packer.rb +41 -41
  49. data/lib/fidgit/elements/radio_button.rb +85 -85
  50. data/lib/fidgit/elements/scroll_area.rb +67 -67
  51. data/lib/fidgit/elements/scroll_bar.rb +127 -127
  52. data/lib/fidgit/elements/scroll_window.rb +82 -82
  53. data/lib/fidgit/elements/slider.rb +124 -124
  54. data/lib/fidgit/elements/text_area.rb +493 -493
  55. data/lib/fidgit/elements/text_line.rb +91 -91
  56. data/lib/fidgit/elements/toggle_button.rb +66 -66
  57. data/lib/fidgit/elements/tool_tip.rb +34 -34
  58. data/lib/fidgit/elements/vertical.rb +11 -11
  59. data/lib/fidgit/event.rb +158 -158
  60. data/lib/fidgit/gosu_ext/color.rb +135 -135
  61. data/lib/fidgit/gosu_ext/gosu_module.rb +24 -24
  62. data/lib/fidgit/history.rb +90 -90
  63. data/lib/fidgit/redirector.rb +82 -82
  64. data/lib/fidgit/schema.rb +123 -123
  65. data/lib/fidgit/selection.rb +105 -105
  66. data/lib/fidgit/standard_ext/hash.rb +20 -20
  67. data/lib/fidgit/states/dialog_state.rb +51 -51
  68. data/lib/fidgit/states/file_dialog.rb +24 -24
  69. data/lib/fidgit/states/gui_state.rb +329 -329
  70. data/lib/fidgit/states/message_dialog.rb +60 -60
  71. data/lib/fidgit/version.rb +4 -4
  72. data/lib/fidgit/window.rb +19 -19
  73. data/spec/fidgit/elements/helpers/helper.rb +2 -2
  74. data/spec/fidgit/elements/helpers/tex_play_helper.rb +8 -8
  75. data/spec/fidgit/elements/image_frame_spec.rb +68 -68
  76. data/spec/fidgit/elements/label_spec.rb +36 -36
  77. data/spec/fidgit/event_spec.rb +209 -209
  78. data/spec/fidgit/gosu_ext/color_spec.rb +129 -129
  79. data/spec/fidgit/gosu_ext/helpers/helper.rb +2 -2
  80. data/spec/fidgit/helpers/helper.rb +3 -3
  81. data/spec/fidgit/history_spec.rb +153 -153
  82. data/spec/fidgit/redirector_spec.rb +77 -77
  83. data/spec/fidgit/schema_spec.rb +66 -66
  84. data/spec/fidgit/schema_test.yml +32 -32
  85. metadata +67 -22
@@ -1,83 +1,83 @@
1
- module Fidgit
2
- # Redirects methods to an object, but does not mask methods and ivars from the calling context.
3
- module RedirectorMethods
4
- # Evaluate a block accessing methods and ivars from the calling context, but calling public methods
5
- # (not ivars or non-public methods) on this object in preference.
6
- def instance_methods_eval(&block)
7
- raise ArgumentError, "block required" unless block_given?
8
-
9
- context = eval('self', block.binding)
10
-
11
- context.send :push_redirection_target, self
12
-
13
- begin
14
- yield context
15
- ensure
16
- context.send :pop_redirection_target
17
- end
18
-
19
- self
20
- end
21
-
22
- protected
23
- def push_redirection_target(target)
24
- meta_class = class << self; self; end
25
- base_methods = Object.public_instance_methods
26
-
27
- # Redirect just the public methods of the target, less those that are on Object.
28
- methods_to_redirect = target.public_methods - base_methods
29
-
30
- # Only hide those public/private/protected methods that are being redirected.
31
- methods_overridden = []
32
- [:public, :protected, :private].each do |access|
33
- methods_to_hide = meta_class.send("#{access}_instance_methods", false) & methods_to_redirect
34
- methods_to_hide.each do |meth|
35
- # Take a reference to the method we are about to override.
36
- methods_overridden.push [meth, method(meth), access]
37
- meta_class.send :remove_method, meth
38
- end
39
- end
40
-
41
- # Add a method, to redirect calls to the target.
42
- methods_to_redirect.each do |meth|
43
- meta_class.send :define_method, meth do |*args, &block|
44
- target.send meth, *args, &block
45
- end
46
- end
47
-
48
- redirection_stack.push [target, methods_overridden, methods_to_redirect]
49
-
50
- target
51
- end
52
-
53
- protected
54
- def pop_redirection_target
55
- meta_class = class << self; self; end
56
-
57
- target, methods_to_recreate, methods_to_remove = redirection_stack.pop
58
-
59
- # Remove the redirection methods
60
- methods_to_remove.reverse_each do |meth|
61
- meta_class.send :remove_method, meth
62
- end
63
-
64
- # Replace with the previous versions of the methods.
65
- methods_to_recreate.reverse_each do |meth, reference, access|
66
- meta_class.send :define_method, meth, reference
67
- meta_class.send access, meth unless access == :public
68
- end
69
-
70
- target
71
- end
72
-
73
- # Direct access to the redirection stack.
74
- private
75
- def redirection_stack
76
- @_redirection_stack ||= []
77
- end
78
- end
79
- end
80
-
81
- class Object
82
- include Fidgit::RedirectorMethods
1
+ module Fidgit
2
+ # Redirects methods to an object, but does not mask methods and ivars from the calling context.
3
+ module RedirectorMethods
4
+ # Evaluate a block accessing methods and ivars from the calling context, but calling public methods
5
+ # (not ivars or non-public methods) on this object in preference.
6
+ def instance_methods_eval(&block)
7
+ raise ArgumentError, "block required" unless block_given?
8
+
9
+ context = eval('self', block.binding)
10
+
11
+ context.send :push_redirection_target, self
12
+
13
+ begin
14
+ yield context
15
+ ensure
16
+ context.send :pop_redirection_target
17
+ end
18
+
19
+ self
20
+ end
21
+
22
+ protected
23
+ def push_redirection_target(target)
24
+ meta_class = class << self; self; end
25
+ base_methods = Object.public_instance_methods
26
+
27
+ # Redirect just the public methods of the target, less those that are on Object.
28
+ methods_to_redirect = target.public_methods - base_methods
29
+
30
+ # Only hide those public/private/protected methods that are being redirected.
31
+ methods_overridden = []
32
+ [:public, :protected, :private].each do |access|
33
+ methods_to_hide = meta_class.send("#{access}_instance_methods", false) & methods_to_redirect
34
+ methods_to_hide.each do |meth|
35
+ # Take a reference to the method we are about to override.
36
+ methods_overridden.push [meth, method(meth), access]
37
+ meta_class.send :remove_method, meth
38
+ end
39
+ end
40
+
41
+ # Add a method, to redirect calls to the target.
42
+ methods_to_redirect.each do |meth|
43
+ meta_class.send :define_method, meth do |*args, &block|
44
+ target.send meth, *args, &block
45
+ end
46
+ end
47
+
48
+ redirection_stack.push [target, methods_overridden, methods_to_redirect]
49
+
50
+ target
51
+ end
52
+
53
+ protected
54
+ def pop_redirection_target
55
+ meta_class = class << self; self; end
56
+
57
+ target, methods_to_recreate, methods_to_remove = redirection_stack.pop
58
+
59
+ # Remove the redirection methods
60
+ methods_to_remove.reverse_each do |meth|
61
+ meta_class.send :remove_method, meth
62
+ end
63
+
64
+ # Replace with the previous versions of the methods.
65
+ methods_to_recreate.reverse_each do |meth, reference, access|
66
+ meta_class.send :define_method, meth, reference
67
+ meta_class.send access, meth unless access == :public
68
+ end
69
+
70
+ target
71
+ end
72
+
73
+ # Direct access to the redirection stack.
74
+ private
75
+ def redirection_stack
76
+ @_redirection_stack ||= []
77
+ end
78
+ end
79
+ end
80
+
81
+ class Object
82
+ include Fidgit::RedirectorMethods
83
83
  end
@@ -1,123 +1,123 @@
1
- # encoding: utf-8
2
-
3
- module Fidgit
4
- # An object that manages Schema values. Usually loaded from a YAML file.
5
- #
6
- # @example
7
- # schema = Schema.new(YAML.load(file.read('default_schema.yml')))
8
- # default_color = schema.default(Element, :disabled, :color)
9
- # schema.merge_schema!(YAML.load(file.read('override_schema.yml'))
10
- # overridden_color = schema.default(Element, :disabled, :color)
11
- class Schema
12
- CONSTANT_PREFIX = '?'
13
-
14
- # @param [Hash<Symbol => Hash>] schema data containing
15
- def initialize(schema)
16
- @constants = {}
17
- @elements = {}
18
-
19
- merge_schema! schema
20
- end
21
-
22
- # Merge in a hash containing constant values.
23
- #
24
- # @param [Hash<Symbol => Hash>] constants_hash Containing :colors, :constants and :elements hashes.
25
- def merge_schema!(schema)
26
- merge_constants!(schema[:constants]) if schema[:constants]
27
- merge_elements!(schema[:elements]) if schema[:elements]
28
-
29
- self
30
- end
31
-
32
- # Merge in a hash containing constant values. Arrays will be resolved as colors in RGBA or RGB format.
33
- #
34
- # @param [Hash<Symbol => Object>] constants_hash
35
- def merge_constants!(constants_hash)
36
- constants_hash.each_pair do |name, value|
37
- @constants[name] = case value
38
- when Array
39
- case value.size
40
- when 3 then Gosu::Color.rgb(*value)
41
- when 4 then Gosu::Color.rgba(*value)
42
- else
43
- raise "Colors must be in 0..255, RGB or RGBA array format"
44
- end
45
- else
46
- value
47
- end
48
- end
49
-
50
- self
51
- end
52
-
53
- # Merge in a hash containing default values for each element.
54
- #
55
- # @param [Hash<Symbol => Hash>] elements_hash
56
- def merge_elements!(elements_hash)
57
- elements_hash.each_pair do |klass_names, data|
58
- klass = Fidgit
59
- klass_names.to_s.split('::').each do |klass_name|
60
- klass = klass.const_get klass_name
61
- end
62
-
63
- raise "elements must be names of classes derived from #{Element}" unless klass.ancestors.include? Fidgit::Element
64
- @elements[klass] ||= {}
65
- @elements[klass].deep_merge! data
66
- end
67
-
68
- self
69
- end
70
-
71
- # Get the constant value associated with +name+.
72
- #
73
- # @param [Symbol] name
74
- # @return [Object]
75
- def constant(name)
76
- @constants[name]
77
- end
78
-
79
- # @param [Class] klass Class to look for defaults for.
80
- # @param [Symbol, Array<Symbol>] names Hash names to search for in that class's schema.
81
- def default(klass, names)
82
- raise ArgumentError, "#{klass} is not a descendent of the #{Element} class" unless klass.ancestors.include? Element
83
- value = default_internal(klass, Array(names), true)
84
- raise("Failed to find named value #{names.inspect} for class #{klass}") unless value
85
- value
86
- end
87
-
88
- protected
89
- # @param [Class] klass Class to look for defaults for.
90
- # @param [Array<Symbol>] names Hash names to search for in that class's schema.
91
- # @param [Boolean] default_to_outer Whether to default to an outer value (used internally)
92
- def default_internal(klass, names, default_to_outer)
93
- # Find the value by moving through the nested hash via the names.
94
- value = @elements[klass]
95
-
96
- names.each do |name|
97
- break unless value.is_a? Hash
98
- value = value.has_key?(name) ? value[name] : nil
99
- end
100
-
101
- # Convert the value to a color/constant if they are symbols.
102
- value = if value.is_a? String and value[0] == CONSTANT_PREFIX
103
- str = value[1..-1]
104
- constant(str.to_sym) || value # If the value isn't a constant, return the string.
105
- else
106
- value
107
- end
108
-
109
- # If we didn't find the value for this class, default to parent class value.
110
- if value.nil? and klass != Element and klass.ancestors.include? Element
111
- # Check if any ancestors define the fully named value.
112
- value = default_internal(klass.superclass, names, false)
113
- end
114
-
115
- if value.nil? and default_to_outer and names.size > 1
116
- # Check the outer values (e.g. if [:hover, :color] is not defined, try [:color]).
117
- value = default_internal(klass, names[1..-1], true)
118
- end
119
-
120
- value
121
- end
122
- end
123
- end
1
+ # encoding: utf-8
2
+
3
+ module Fidgit
4
+ # An object that manages Schema values. Usually loaded from a YAML file.
5
+ #
6
+ # @example
7
+ # schema = Schema.new(YAML.load(file.read('default_schema.yml')))
8
+ # default_color = schema.default(Element, :disabled, :color)
9
+ # schema.merge_schema!(YAML.load(file.read('override_schema.yml'))
10
+ # overridden_color = schema.default(Element, :disabled, :color)
11
+ class Schema
12
+ CONSTANT_PREFIX = '?'
13
+
14
+ # @param [Hash<Symbol => Hash>] schema data containing
15
+ def initialize(schema)
16
+ @constants = {}
17
+ @elements = {}
18
+
19
+ merge_schema! schema
20
+ end
21
+
22
+ # Merge in a hash containing constant values.
23
+ #
24
+ # @param [Hash<Symbol => Hash>] constants_hash Containing :colors, :constants and :elements hashes.
25
+ def merge_schema!(schema)
26
+ merge_constants!(schema[:constants]) if schema[:constants]
27
+ merge_elements!(schema[:elements]) if schema[:elements]
28
+
29
+ self
30
+ end
31
+
32
+ # Merge in a hash containing constant values. Arrays will be resolved as colors in RGBA or RGB format.
33
+ #
34
+ # @param [Hash<Symbol => Object>] constants_hash
35
+ def merge_constants!(constants_hash)
36
+ constants_hash.each_pair do |name, value|
37
+ @constants[name] = case value
38
+ when Array
39
+ case value.size
40
+ when 3 then Gosu::Color.rgb(*value)
41
+ when 4 then Gosu::Color.rgba(*value)
42
+ else
43
+ raise "Colors must be in 0..255, RGB or RGBA array format"
44
+ end
45
+ else
46
+ value
47
+ end
48
+ end
49
+
50
+ self
51
+ end
52
+
53
+ # Merge in a hash containing default values for each element.
54
+ #
55
+ # @param [Hash<Symbol => Hash>] elements_hash
56
+ def merge_elements!(elements_hash)
57
+ elements_hash.each_pair do |klass_names, data|
58
+ klass = Fidgit
59
+ klass_names.to_s.split('::').each do |klass_name|
60
+ klass = klass.const_get klass_name
61
+ end
62
+
63
+ raise "elements must be names of classes derived from #{Element}" unless klass.ancestors.include? Fidgit::Element
64
+ @elements[klass] ||= {}
65
+ @elements[klass].deep_merge! data
66
+ end
67
+
68
+ self
69
+ end
70
+
71
+ # Get the constant value associated with +name+.
72
+ #
73
+ # @param [Symbol] name
74
+ # @return [Object]
75
+ def constant(name)
76
+ @constants[name]
77
+ end
78
+
79
+ # @param [Class] klass Class to look for defaults for.
80
+ # @param [Symbol, Array<Symbol>] names Hash names to search for in that class's schema.
81
+ def default(klass, names)
82
+ raise ArgumentError, "#{klass} is not a descendent of the #{Element} class" unless klass.ancestors.include? Element
83
+ value = default_internal(klass, Array(names), true)
84
+ raise("Failed to find named value #{names.inspect} for class #{klass}") unless value
85
+ value
86
+ end
87
+
88
+ protected
89
+ # @param [Class] klass Class to look for defaults for.
90
+ # @param [Array<Symbol>] names Hash names to search for in that class's schema.
91
+ # @param [Boolean] default_to_outer Whether to default to an outer value (used internally)
92
+ def default_internal(klass, names, default_to_outer)
93
+ # Find the value by moving through the nested hash via the names.
94
+ value = @elements[klass]
95
+
96
+ names.each do |name|
97
+ break unless value.is_a? Hash
98
+ value = value.has_key?(name) ? value[name] : nil
99
+ end
100
+
101
+ # Convert the value to a color/constant if they are symbols.
102
+ value = if value.is_a? String and value[0] == CONSTANT_PREFIX
103
+ str = value[1..-1]
104
+ constant(str.to_sym) || value # If the value isn't a constant, return the string.
105
+ else
106
+ value
107
+ end
108
+
109
+ # If we didn't find the value for this class, default to parent class value.
110
+ if value.nil? and klass != Element and klass.ancestors.include? Element
111
+ # Check if any ancestors define the fully named value.
112
+ value = default_internal(klass.superclass, names, false)
113
+ end
114
+
115
+ if value.nil? and default_to_outer and names.size > 1
116
+ # Check the outer values (e.g. if [:hover, :color] is not defined, try [:color]).
117
+ value = default_internal(klass, names[1..-1], true)
118
+ end
119
+
120
+ value
121
+ end
122
+ end
123
+ end
@@ -1,106 +1,106 @@
1
- # encoding: utf-8
2
-
3
- module Fidgit
4
- class Selection
5
- MIN_DRAG_DISTANCE = 2
6
-
7
- def size; @items.size; end
8
- def empty?; @items.empty?; end
9
- def [](index); @items[index]; end
10
- def each(&block); @items.each(&block); end
11
- def to_a; @items.dup; end
12
- def include?(object); @items.include? object; end
13
-
14
- # Current being dragged?
15
- def dragging?; @dragging; end
16
-
17
- # Actually moved during a dragging operation?
18
- def moved?; @moved; end
19
-
20
- def initialize
21
- @items = []
22
- @moved = false
23
- @dragging = false
24
- end
25
-
26
- def add(object)
27
- object.selected = true
28
- @items.push(object)
29
-
30
- self
31
- end
32
-
33
- def remove(object)
34
- @items.delete(object)
35
- object.selected = false
36
- object.dragging = false
37
-
38
- self
39
- end
40
-
41
- def clear
42
- end_drag if dragging?
43
- @items.each { |o| o.selected = false; o.dragging = false }
44
- @items.clear
45
-
46
- self
47
- end
48
-
49
- def begin_drag(x, y)
50
- @initial_x, @initial_y = x, y
51
- @last_x, @last_y = x, y
52
- @dragging = true
53
- @moved = false
54
-
55
- self
56
- end
57
-
58
- def end_drag
59
- @items.each do |object|
60
- object.x, object.y = object.x.round, object.y.round
61
- object.dragging = false
62
- end
63
- @dragging = false
64
- @moved = false
65
-
66
- self
67
- end
68
-
69
- # Move all dragged object back to original positions.
70
- def reset_drag
71
- if moved?
72
- @items.each do |o|
73
- o.x += @initial_x - @last_x
74
- o.y += @initial_y - @last_y
75
- end
76
- end
77
-
78
- self.end_drag
79
-
80
- self
81
- end
82
-
83
- def update_drag(x, y)
84
- x, y = x.round, y.round
85
-
86
- # If the mouse has been dragged far enough from the initial click position, then 'pick up' the objects and drag.
87
- unless moved?
88
- if distance(@initial_x, @initial_y, x, y) > MIN_DRAG_DISTANCE
89
- @items.each { |o| o.dragging = true }
90
- @moved = true
91
- end
92
- end
93
-
94
- if moved?
95
- @items.each do |o|
96
- o.x += x - @last_x
97
- o.y += y - @last_y
98
- end
99
-
100
- @last_x, @last_y = x, y
101
- end
102
-
103
- self
104
- end
105
- end
1
+ # encoding: utf-8
2
+
3
+ module Fidgit
4
+ class Selection
5
+ MIN_DRAG_DISTANCE = 2
6
+
7
+ def size; @items.size; end
8
+ def empty?; @items.empty?; end
9
+ def [](index); @items[index]; end
10
+ def each(&block); @items.each(&block); end
11
+ def to_a; @items.dup; end
12
+ def include?(object); @items.include? object; end
13
+
14
+ # Current being dragged?
15
+ def dragging?; @dragging; end
16
+
17
+ # Actually moved during a dragging operation?
18
+ def moved?; @moved; end
19
+
20
+ def initialize
21
+ @items = []
22
+ @moved = false
23
+ @dragging = false
24
+ end
25
+
26
+ def add(object)
27
+ object.selected = true
28
+ @items.push(object)
29
+
30
+ self
31
+ end
32
+
33
+ def remove(object)
34
+ @items.delete(object)
35
+ object.selected = false
36
+ object.dragging = false
37
+
38
+ self
39
+ end
40
+
41
+ def clear
42
+ end_drag if dragging?
43
+ @items.each { |o| o.selected = false; o.dragging = false }
44
+ @items.clear
45
+
46
+ self
47
+ end
48
+
49
+ def begin_drag(x, y)
50
+ @initial_x, @initial_y = x, y
51
+ @last_x, @last_y = x, y
52
+ @dragging = true
53
+ @moved = false
54
+
55
+ self
56
+ end
57
+
58
+ def end_drag
59
+ @items.each do |object|
60
+ object.x, object.y = object.x.round, object.y.round
61
+ object.dragging = false
62
+ end
63
+ @dragging = false
64
+ @moved = false
65
+
66
+ self
67
+ end
68
+
69
+ # Move all dragged object back to original positions.
70
+ def reset_drag
71
+ if moved?
72
+ @items.each do |o|
73
+ o.x += @initial_x - @last_x
74
+ o.y += @initial_y - @last_y
75
+ end
76
+ end
77
+
78
+ self.end_drag
79
+
80
+ self
81
+ end
82
+
83
+ def update_drag(x, y)
84
+ x, y = x.round, y.round
85
+
86
+ # If the mouse has been dragged far enough from the initial click position, then 'pick up' the objects and drag.
87
+ unless moved?
88
+ if distance(@initial_x, @initial_y, x, y) > MIN_DRAG_DISTANCE
89
+ @items.each { |o| o.dragging = true }
90
+ @moved = true
91
+ end
92
+ end
93
+
94
+ if moved?
95
+ @items.each do |o|
96
+ o.x += x - @last_x
97
+ o.y += y - @last_y
98
+ end
99
+
100
+ @last_x, @last_y = x, y
101
+ end
102
+
103
+ self
104
+ end
105
+ end
106
106
  end