vedeu 0.1.18 → 0.1.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -0
  3. data/Dockerfile +40 -0
  4. data/README.md +7 -30
  5. data/docs/api.md +79 -0
  6. data/docs/events.md +121 -0
  7. data/lib/vedeu.rb +61 -18
  8. data/lib/vedeu/api/api.rb +73 -53
  9. data/lib/vedeu/api/composition.rb +4 -1
  10. data/lib/vedeu/api/defined.rb +35 -0
  11. data/lib/vedeu/api/helpers.rb +20 -15
  12. data/lib/vedeu/api/interface.rb +17 -12
  13. data/lib/vedeu/api/line.rb +20 -12
  14. data/lib/vedeu/api/stream.rb +3 -0
  15. data/lib/vedeu/application.rb +34 -1
  16. data/lib/vedeu/configuration.rb +15 -3
  17. data/lib/vedeu/input/input.rb +77 -0
  18. data/lib/vedeu/launcher.rb +7 -0
  19. data/lib/vedeu/models/attributes/background.rb +15 -2
  20. data/lib/vedeu/models/attributes/coercions.rb +18 -3
  21. data/lib/vedeu/models/attributes/colour_translator.rb +23 -17
  22. data/lib/vedeu/models/attributes/foreground.rb +10 -2
  23. data/lib/vedeu/models/attributes/presentation.rb +62 -0
  24. data/lib/vedeu/models/colour.rb +7 -3
  25. data/lib/vedeu/models/composition.rb +17 -19
  26. data/lib/vedeu/models/geometry.rb +13 -5
  27. data/lib/vedeu/models/interface.rb +35 -19
  28. data/lib/vedeu/models/line.rb +24 -8
  29. data/lib/vedeu/models/stream.rb +13 -7
  30. data/lib/vedeu/models/style.rb +17 -7
  31. data/lib/vedeu/{support → output}/clear.rb +14 -0
  32. data/lib/vedeu/output/compositor.rb +77 -0
  33. data/lib/vedeu/output/refresh.rb +129 -0
  34. data/lib/vedeu/{support → output}/render.rb +49 -13
  35. data/lib/vedeu/{support → output}/view.rb +15 -8
  36. data/lib/vedeu/repositories/buffers.rb +181 -0
  37. data/lib/vedeu/{support → repositories}/events.rb +16 -6
  38. data/lib/vedeu/repositories/focus.rb +109 -0
  39. data/lib/vedeu/repositories/groups.rb +76 -0
  40. data/lib/vedeu/repositories/interfaces.rb +74 -0
  41. data/lib/vedeu/support/common.rb +20 -0
  42. data/lib/vedeu/support/cursor.rb +77 -0
  43. data/lib/vedeu/support/esc.rb +181 -46
  44. data/lib/vedeu/support/event.rb +22 -4
  45. data/lib/vedeu/support/grid.rb +10 -3
  46. data/lib/vedeu/support/log.rb +14 -1
  47. data/lib/vedeu/support/menu.rb +51 -12
  48. data/lib/vedeu/support/position.rb +9 -0
  49. data/lib/vedeu/support/terminal.rb +49 -15
  50. data/lib/vedeu/support/trace.rb +11 -4
  51. data/test/integration/defining_interfaces_test.rb +27 -0
  52. data/test/integration/views/basic_view_test.rb +767 -0
  53. data/test/lib/vedeu/api/api_test.rb +32 -37
  54. data/test/lib/vedeu/api/composition_test.rb +23 -61
  55. data/test/lib/vedeu/api/defined_test.rb +49 -0
  56. data/test/lib/vedeu/api/helpers_test.rb +91 -0
  57. data/test/lib/vedeu/api/interface_test.rb +136 -688
  58. data/test/lib/vedeu/api/line_test.rb +28 -32
  59. data/test/lib/vedeu/application_test.rb +6 -0
  60. data/test/lib/vedeu/configuration_test.rb +8 -4
  61. data/test/lib/vedeu/{support → input}/input_test.rb +9 -0
  62. data/test/lib/vedeu/launcher_test.rb +6 -0
  63. data/test/lib/vedeu/models/attributes/{coercer_test.rb → coercions_test.rb} +11 -10
  64. data/test/lib/vedeu/models/attributes/colour_translator_test.rb +13 -0
  65. data/test/lib/vedeu/models/attributes/presentation_test.rb +30 -0
  66. data/test/lib/vedeu/models/colour_test.rb +8 -0
  67. data/test/lib/vedeu/models/composition_test.rb +208 -200
  68. data/test/lib/vedeu/models/geometry_test.rb +39 -0
  69. data/test/lib/vedeu/models/interface_test.rb +11 -1
  70. data/test/lib/vedeu/models/line_test.rb +8 -1
  71. data/test/lib/vedeu/models/stream_test.rb +35 -0
  72. data/test/lib/vedeu/models/style_test.rb +8 -0
  73. data/test/lib/vedeu/{support → output}/clear_test.rb +1 -1
  74. data/test/lib/vedeu/output/compositor_test.rb +64 -0
  75. data/test/lib/vedeu/output/refresh_test.rb +48 -0
  76. data/test/lib/vedeu/{support → output}/render_test.rb +36 -0
  77. data/test/lib/vedeu/{support → output}/view_test.rb +0 -0
  78. data/test/lib/vedeu/repositories/buffers_test.rb +48 -0
  79. data/test/lib/vedeu/{support → repositories}/events_test.rb +0 -0
  80. data/test/lib/vedeu/repositories/focus_test.rb +74 -0
  81. data/test/lib/vedeu/repositories/groups_test.rb +66 -0
  82. data/test/lib/vedeu/repositories/interfaces_test.rb +6 -0
  83. data/test/lib/vedeu/support/common_test.rb +6 -0
  84. data/test/lib/vedeu/support/cursor_test.rb +79 -0
  85. data/test/lib/vedeu/support/log_test.rb +6 -0
  86. data/test/lib/vedeu/support/terminal_test.rb +6 -28
  87. data/test/lib/vedeu/support/trace_test.rb +6 -0
  88. data/test/test_helper.rb +37 -0
  89. data/vedeu.gemspec +1 -1
  90. metadata +65 -33
  91. data/bin/log +0 -13
  92. data/lib/vedeu/support/buffer.rb +0 -69
  93. data/lib/vedeu/support/buffers.rb +0 -106
  94. data/lib/vedeu/support/focus.rb +0 -83
  95. data/lib/vedeu/support/groups.rb +0 -61
  96. data/lib/vedeu/support/input.rb +0 -67
  97. data/test/lib/vedeu/support/buffer_test.rb +0 -83
  98. data/test/lib/vedeu/support/buffers_test.rb +0 -15
  99. data/test/lib/vedeu/support/focus_test.rb +0 -114
  100. data/test/lib/vedeu/support/groups_test.rb +0 -65
@@ -0,0 +1,129 @@
1
+ module Vedeu
2
+
3
+ # Refreshes the terminal.
4
+ #
5
+ # @api private
6
+ module Refresh
7
+
8
+ include Vedeu::Common
9
+ extend self
10
+
11
+ # Add a refresh event for the interface.
12
+ #
13
+ # @param attributes [Hash]
14
+ # @return [TrueClass|FalseClass]
15
+ def add_interface(attributes)
16
+ register_by_name(attributes[:name], attributes[:delay])
17
+ end
18
+
19
+ # Add a refresh event for the group.
20
+ #
21
+ # @param attributes [Hash]
22
+ # @return [TrueClass|FalseClass]
23
+ def add_group(attributes)
24
+ register_by_group(attributes[:group], attributes[:delay])
25
+ end
26
+
27
+ # Refresh all registered interfaces.
28
+ #
29
+ # @return [Array]
30
+ def all
31
+ Vedeu::Interfaces.registered.each { |name| by_name(name) }
32
+ end
33
+
34
+ # Refresh the interface which is currently focussed.
35
+ #
36
+ # @return [|NoInterfacesDefined]
37
+ def by_focus
38
+ by_name(Vedeu::Focus.current)
39
+ end
40
+
41
+ # Refresh an interface, or collection of interfaces belonging to a group.
42
+ #
43
+ # @param group_name [String] The name of the group to be refreshed.
44
+ # @return [Array|GroupNotFound] A collection of the names of interfaces
45
+ # refreshed, or an exception if the group was not found.
46
+ def by_group(group_name)
47
+ Vedeu::Groups.find(group_name).each { |name| by_name(name) }
48
+ end
49
+
50
+ # Refresh an interface by name.
51
+ #
52
+ # @param name [String] The name of the interface to be refreshed.
53
+ # @return [|BufferNotFound]
54
+ def by_name(name)
55
+ Vedeu::Compositor.render(name)
56
+ end
57
+
58
+ private
59
+
60
+ # Register a refresh event for an interface by name. When the event is
61
+ # called, the interface with this name will be refreshed.
62
+ #
63
+ # @api private
64
+ # @param name [String] The name of the interface to be refreshed.
65
+ # @param delay [Float] The throttle for how often this interface can be
66
+ # refreshed.
67
+ # @return []
68
+ def register_by_name(name, delay)
69
+ return false unless defined_value?(name)
70
+ return false if event_registered?(event_name(name))
71
+
72
+ Vedeu.event(event_name(name), { delay: delay }) do
73
+ Vedeu::Refresh.by_name(name)
74
+ end
75
+
76
+ true
77
+ end
78
+
79
+ # Register a refresh event for a group of interfaces by name. When the event
80
+ # is called, all interfaces belonging to the group with this name will be
81
+ # refreshed.
82
+ #
83
+ # @api private
84
+ # @param name [String] The name of the group to be refreshed.
85
+ # @param delay [Float] The throttle for how often this group can be
86
+ # refreshed.
87
+ # @return []
88
+ def register_by_group(name, delay)
89
+ return false unless defined_value?(name)
90
+ return false if event_registered?(group_event_name(name))
91
+
92
+ Vedeu.event(group_event_name(name), { delay: delay }) do
93
+ Vedeu::Refresh.by_group(name)
94
+ end
95
+
96
+ true
97
+ end
98
+
99
+ # Returns the event name for refreshing the named interface.
100
+ #
101
+ # @api private
102
+ # @param name [String] The name of the interface.
103
+ # @return [Symbol]
104
+ def event_name(name)
105
+ "_refresh_#{name}_".to_sym
106
+ end
107
+
108
+ # Returns the event name for refreshing the named group of interfaces.
109
+ #
110
+ # @api private
111
+ # @param name [String] The name of the group.
112
+ # @return [Symbol]
113
+ def group_event_name(name)
114
+ "_refresh_group_#{name}_".to_sym
115
+ end
116
+
117
+ # Returns a boolean indicating that an event by name has already been
118
+ # registered.
119
+ #
120
+ # @api private
121
+ # @param name [Symbol] The event name to check.
122
+ # @return [TrueClass|FalseClass]
123
+ def event_registered?(name)
124
+ Vedeu.events.registered?(name)
125
+ end
126
+
127
+ end
128
+
129
+ end
@@ -1,23 +1,32 @@
1
1
  module Vedeu
2
+
3
+ # Attempts to convert the provided interface object with associated lines,
4
+ # streams, colours, styles, etc, into a single string containing all content
5
+ # and escape sequences.
6
+ #
7
+ # @api private
2
8
  class Render
3
9
 
4
- # Attempts to convert the provided interface object with associated lines,
5
- # streams, colours, styles, etc, into a single string containing all content
6
- # and escape sequences.
10
+ # Create a new instance of Render with the provided {Vedeu::Interface} and
11
+ # then convert the interface into a single string of content and escape
12
+ # sequences.
7
13
  #
8
14
  # @api private
9
15
  # @param interface [Interface]
16
+ # @param options [Hash]
10
17
  # @return [String]
11
- def self.call(interface)
12
- new(interface).render
18
+ def self.call(interface, options = {})
19
+ new(interface, options).render
13
20
  end
14
21
 
15
- # Initializes a new Render object with the provided interface.
22
+ # Return a new instance of Render.
16
23
  #
17
24
  # @param interface [Interface]
25
+ # @param options [Hash]
18
26
  # @return [Render]
19
- def initialize(interface)
27
+ def initialize(interface, options = {})
20
28
  @interface = interface
29
+ @options = options
21
30
  end
22
31
 
23
32
  # Produces a single string which contains all content and escape sequences
@@ -27,10 +36,8 @@ module Vedeu
27
36
  def render
28
37
  out = [ Clear.call(interface) ]
29
38
  processed_lines.each_with_index do |line, index|
30
- if index + 1 <= height
31
- out << interface.origin(index)
32
- out << line.to_s
33
- end
39
+ out << interface.origin(index)
40
+ out << line.to_s
34
41
  end
35
42
  out << interface.cursor
36
43
  out.join
@@ -63,6 +70,7 @@ module Vedeu
63
70
  colour: stream.colour.attributes,
64
71
  style: stream.style.values,
65
72
  text: truncate(stream.text, remainder),
73
+ parent: line.view_attributes,
66
74
  })
67
75
 
68
76
  else
@@ -75,6 +83,7 @@ module Vedeu
75
83
  colour: line.colour.attributes,
76
84
  streams: processed,
77
85
  style: line.style.values,
86
+ parent: interface.view_attributes,
78
87
  })
79
88
 
80
89
  else
@@ -106,19 +115,24 @@ module Vedeu
106
115
  end
107
116
 
108
117
  # Provides a collection of lines associated with the interface.
118
+ # If the option `:top` was set, we will start at that line. Any lines
119
+ # outside of the height will not be rendered.
109
120
  #
110
121
  # @api private
111
122
  # @return [Array]
112
123
  def lines
113
- interface.lines
124
+ interface.lines[top..height]
114
125
  end
115
126
 
116
127
  # Provides the currently available height of the interface.
117
128
  #
129
+ # @note The height is reported to be one less line than actual because
130
+ # terminal coordinates count from 1, not 0.
131
+ #
118
132
  # @api private
119
133
  # @return [Fixnum]
120
134
  def height
121
- interface.viewport_height
135
+ interface.viewport_height - 1
122
136
  end
123
137
 
124
138
  # Provides the currently available width of the interface.
@@ -129,5 +143,27 @@ module Vedeu
129
143
  interface.viewport_width
130
144
  end
131
145
 
146
+ # The current top line.
147
+ #
148
+ # @api private
149
+ # @return [Fixnum]
150
+ def top
151
+ options[:top]
152
+ end
153
+
154
+ # @api private
155
+ # @return [Hash]
156
+ def options
157
+ @_options ||= defaults.merge!(@options)
158
+ end
159
+
160
+ # @api private
161
+ # @return [Hash]
162
+ def defaults
163
+ {
164
+ top: 0
165
+ }
166
+ end
167
+
132
168
  end
133
169
  end
@@ -1,4 +1,9 @@
1
1
  module Vedeu
2
+
3
+ # Subclassing Vedeu::View will allow Vedeu to call your #render method.
4
+ # This method should contain attributes required to build a view or views.
5
+ # These attributes will be added to the back buffer of each interface
6
+ # mentioned, to be rendered upon next refresh.
2
7
  class View
3
8
 
4
9
  include Vedeu::API
@@ -9,6 +14,8 @@ module Vedeu
9
14
  new(object).enqueue
10
15
  end
11
16
 
17
+ # Returns a new instance of View.
18
+ #
12
19
  # @param object []
13
20
  # @return [View]
14
21
  def initialize(object = nil)
@@ -17,31 +24,31 @@ module Vedeu
17
24
 
18
25
  # @return [Array]
19
26
  def enqueue
20
- interfaces.map { |interface| Buffers.enqueue(interface.name, interface) }
27
+ composition.interfaces.map do |interface|
28
+ Buffers.add(interface.attributes)
29
+ end
21
30
  end
22
31
 
23
32
  # @return [Exception]
24
33
  def render
25
34
  fail NotImplemented, 'Implement #render on your subclass of Vedeu::View.'
26
35
  end
27
- alias_method :output, :render
28
36
 
29
37
  private
30
38
 
31
39
  attr_reader :object
32
40
 
33
- # @api private
34
- # @return [Array]
35
- def interfaces
36
- composition.interfaces
37
- end
38
-
41
+ # Create a new Composition object with the attributes.
42
+ #
39
43
  # @api private
40
44
  # @return [Composition]
41
45
  def composition
42
46
  @_composition ||= Composition.new(attributes)
43
47
  end
44
48
 
49
+ # Calls the #render method of the subclass, hopefully receives attributes
50
+ # suitable to create one or more views.
51
+ #
45
52
  # @api private
46
53
  # @return []
47
54
  def attributes
@@ -0,0 +1,181 @@
1
+ module Vedeu
2
+
3
+ # Stores interface views to be later combined with interface geometry to be
4
+ # displayed.
5
+ #
6
+ # @api private
7
+ module Buffers
8
+
9
+ include Vedeu::Common
10
+ extend self
11
+
12
+ # @param attributes [Hash]
13
+ # @return [Hash]
14
+ def create(attributes)
15
+ add(attributes)
16
+
17
+ Vedeu::Interfaces.add(attributes)
18
+ Vedeu::Refresh.add_interface(attributes)
19
+
20
+ Vedeu::Groups.add(attributes)
21
+ Vedeu::Refresh.add_group(attributes)
22
+
23
+ Vedeu::Focus.add(attributes)
24
+ end
25
+
26
+ # Add an interface view into the back buffer. If the buffer is already
27
+ # registered, then we preserve its front buffer.
28
+ #
29
+ # @param attributes [Hash]
30
+ # @return [Hash]
31
+ def add(attributes)
32
+ if registered?(attributes[:name])
33
+ buffer = find(attributes[:name])
34
+ buffer[:back_buffer] = attributes
35
+
36
+ else
37
+ storage.store(attributes[:name], {
38
+ back_buffer: attributes,
39
+ front_buffer: nil,
40
+ })
41
+
42
+ end
43
+
44
+ storage
45
+ end
46
+
47
+ # Find the buffer by name.
48
+ #
49
+ # @param name [String]
50
+ # @return [Hash]
51
+ def find(name)
52
+ storage.fetch(name) do
53
+ fail BufferNotFound, "Cannot find buffer with this name: #{name.to_s}."
54
+ end
55
+ end
56
+
57
+ # Returns the latest content for the named buffer. The latest content always
58
+ # goes on to the back buffer. The content which was last output is on the
59
+ # front buffer.
60
+ #
61
+ # When the back buffer has new content, we swap the back onto the front and
62
+ # return the front buffer to be rendered.
63
+ #
64
+ # When the back buffer has no new content, we display that which we
65
+ # previously displayed, by returning the front buffer.
66
+ #
67
+ # If both the back and front buffers have no content, then the view is blank
68
+ # and we should return nothing.
69
+ #
70
+ # @param name [String]
71
+ # @return [Hash]
72
+ def latest(name)
73
+ if new_content?(name)
74
+ swap_buffers(name)
75
+ content(name)
76
+
77
+ elsif old_content?(name)
78
+ content(name)
79
+
80
+ else
81
+ nil
82
+
83
+ end
84
+ end
85
+
86
+ # Returns the named front buffer.
87
+ #
88
+ # @param name [String]
89
+ # @return [Hash]
90
+ def content(name)
91
+ front_buffer(name)
92
+ end
93
+
94
+ # Swap the named back buffer into the front buffer of the same name.
95
+ #
96
+ # @param name [String]
97
+ # @return [Hash]
98
+ def swap_buffers(name)
99
+ buffer = find(name)
100
+
101
+ storage.store(name, {
102
+ front_buffer: buffer[:back_buffer],
103
+ back_buffer: nil,
104
+ })
105
+ end
106
+
107
+ # Reset the buffers repository; removing all buffers. This does not delete
108
+ # the interfaces themselves.
109
+ #
110
+ # @return [Hash]
111
+ def reset
112
+ @_storage = in_memory
113
+ end
114
+
115
+ private
116
+
117
+ # Return a boolean indicating whether the named back buffer has new content.
118
+ #
119
+ # @api private
120
+ # @param name [String]
121
+ # @return [TrueClass|FalseClass]
122
+ def new_content?(name)
123
+ defined_value?(back_buffer(name))
124
+ end
125
+
126
+ # Return a boolean indicating whether the named front buffer has content.
127
+ #
128
+ # @api private
129
+ # @param name [String]
130
+ # @return [TrueClass|FalseClass]
131
+ def old_content?(name)
132
+ defined_value?(front_buffer(name))
133
+ end
134
+
135
+ # Return the named back buffer.
136
+ #
137
+ # @api private
138
+ # @param name [String]
139
+ # @return [Hash|Nil]
140
+ def back_buffer(name)
141
+ find(name).fetch(:back_buffer, nil)
142
+ end
143
+
144
+ # Return the named front buffer.
145
+ #
146
+ # @api private
147
+ # @param name [String]
148
+ # @return [Hash|Nil]
149
+ def front_buffer(name)
150
+ find(name).fetch(:front_buffer, nil)
151
+ end
152
+
153
+ # Returns a boolean indicating whether the named buffer is registered.
154
+ #
155
+ # @api private
156
+ # @param name [String]
157
+ # @return [TrueClass|FalseClass]
158
+ def registered?(name)
159
+ storage.key?(name)
160
+ end
161
+
162
+ # @api private
163
+ # @return [Hash]
164
+ def storage
165
+ @_storage ||= in_memory
166
+ end
167
+
168
+ # @api private
169
+ # @return [Hash]
170
+ def in_memory
171
+ Hash.new do |hash, interface_name|
172
+ hash[interface_name] = {
173
+ front_buffer: nil,
174
+ back_buffer: nil,
175
+ }
176
+ end
177
+ end
178
+
179
+ end
180
+
181
+ end