vedeu 0.2.0 → 0.2.1

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -9
  3. data/docs/api.md +70 -42
  4. data/elements.txt +118 -0
  5. data/lib/vedeu.rb +14 -4
  6. data/lib/vedeu/api/api.rb +67 -24
  7. data/lib/vedeu/api/composition.rb +19 -0
  8. data/lib/vedeu/api/defined.rb +7 -0
  9. data/lib/vedeu/api/interface.rb +103 -75
  10. data/lib/vedeu/api/keymap.rb +62 -0
  11. data/lib/vedeu/api/menu.rb +3 -1
  12. data/lib/vedeu/configuration.rb +24 -6
  13. data/lib/vedeu/models/attributes/coercions.rb +18 -26
  14. data/lib/vedeu/models/attributes/colour_translator.rb +7 -7
  15. data/lib/vedeu/models/attributes/presentation.rb +12 -1
  16. data/lib/vedeu/models/geometry.rb +5 -1
  17. data/lib/vedeu/models/interface.rb +6 -47
  18. data/lib/vedeu/models/keymap.rb +66 -0
  19. data/lib/vedeu/models/line.rb +3 -1
  20. data/lib/vedeu/models/stream.rb +10 -1
  21. data/lib/vedeu/models/style.rb +10 -1
  22. data/lib/vedeu/output/compositor.rb +3 -0
  23. data/lib/vedeu/output/refresh.rb +16 -74
  24. data/lib/vedeu/output/render.rb +44 -17
  25. data/lib/vedeu/output/view.rb +3 -0
  26. data/lib/vedeu/repositories/buffers.rb +32 -42
  27. data/lib/vedeu/repositories/events.rb +8 -11
  28. data/lib/vedeu/repositories/focus.rb +15 -13
  29. data/lib/vedeu/repositories/groups.rb +20 -2
  30. data/lib/vedeu/repositories/interfaces.rb +22 -2
  31. data/lib/vedeu/repositories/keymap_validator.rb +104 -0
  32. data/lib/vedeu/repositories/keymaps.rb +239 -0
  33. data/lib/vedeu/repositories/menus.rb +12 -3
  34. data/lib/vedeu/support/common.rb +2 -2
  35. data/lib/vedeu/support/cursor.rb +3 -0
  36. data/lib/vedeu/support/event.rb +48 -7
  37. data/lib/vedeu/support/grid.rb +1 -1
  38. data/lib/vedeu/support/registrar.rb +66 -0
  39. data/lib/vedeu/support/trace.rb +71 -12
  40. data/test/lib/vedeu/api/api_test.rb +27 -9
  41. data/test/lib/vedeu/api/composition_test.rb +10 -0
  42. data/test/lib/vedeu/api/defined_test.rb +14 -0
  43. data/test/lib/vedeu/api/interface_test.rb +86 -85
  44. data/test/lib/vedeu/api/keymap_test.rb +61 -0
  45. data/test/lib/vedeu/configuration_test.rb +12 -0
  46. data/test/lib/vedeu/models/attributes/coercions_test.rb +3 -4
  47. data/test/lib/vedeu/models/interface_test.rb +0 -43
  48. data/test/lib/vedeu/models/keymap_test.rb +19 -0
  49. data/test/lib/vedeu/models/style_test.rb +10 -0
  50. data/test/lib/vedeu/output/refresh_test.rb +0 -12
  51. data/test/lib/vedeu/output/render_test.rb +51 -0
  52. data/test/lib/vedeu/repositories/buffers_test.rb +39 -12
  53. data/test/lib/vedeu/repositories/events_test.rb +6 -0
  54. data/test/lib/vedeu/repositories/focus_test.rb +12 -0
  55. data/test/lib/vedeu/repositories/keymap_validator_test.rb +81 -0
  56. data/test/lib/vedeu/repositories/keymaps_test.rb +254 -0
  57. data/test/lib/vedeu/support/common_test.rb +26 -0
  58. data/test/lib/vedeu/support/registrar_test.rb +68 -0
  59. data/vedeu.gemspec +1 -1
  60. metadata +18 -2
@@ -4,6 +4,25 @@ module Vedeu
4
4
  # @see Vedeu::Composition
5
5
  class Composition < Vedeu::Composition
6
6
 
7
+ # Directly write a view buffer to the terminal.
8
+ #
9
+ # @api public
10
+ # @param block [Proc]
11
+ # @return [Array] A collection of strings, each defining containing the
12
+ # escape sequences and content. This data has already
13
+ # been sent to the terminal to be output.
14
+ def self.render(&block)
15
+ fail InvalidSyntax, '`render` requires a block.' unless block_given?
16
+
17
+ attributes = API::Composition.build({}, &block)
18
+
19
+ Vedeu::Composition.new(attributes).interfaces.map do |interface|
20
+ Buffers.add(interface.attributes)
21
+
22
+ interface.name
23
+ end.map { |name| Compositor.render(name) }
24
+ end
25
+
7
26
  # @api public
8
27
  # @see Vedeu::API#view
9
28
  def view(name, &block)
@@ -30,6 +30,13 @@ module Vedeu
30
30
  Vedeu::Interfaces.registered
31
31
  end
32
32
 
33
+ # Returns all menus currently registered with Vedeu.
34
+ #
35
+ # @return [Array]
36
+ def menus
37
+ Vedeu::Menus.registered
38
+ end
39
+
33
40
  end
34
41
  end
35
42
  end
@@ -6,45 +6,24 @@ module Vedeu
6
6
 
7
7
  include Helpers
8
8
 
9
- # Define a single line in a view.
9
+ # Instructs Vedeu to calculate x and y geometry automatically based on the
10
+ # centre character of the terminal, the width and the height.
10
11
  #
11
12
  # @api public
12
- # @param value [String]
13
- # @param block [Proc]
13
+ # @param value [Boolean]
14
14
  #
15
15
  # @example
16
- # view 'my_interface' do
17
- # line 'This is a line of text...'
18
- # line 'and so is this...'
16
+ # interface 'my_interface' do
17
+ # centred true
19
18
  # ...
20
19
  #
21
- # view 'my_interface' do
22
- # line do
23
- # ... some line attributes ...
24
- # end
25
- # end
26
- #
27
20
  # @return [API::Interface]
28
- def line(value = '', &block)
29
- if block_given?
30
- attributes[:lines] << API::Line
31
- .build({ parent: self.view_attributes }, &block)
32
-
33
- else
34
- attributes[:lines] << API::Line
35
- .build({ streams: { text: value }, parent: self.view_attributes })
36
-
21
+ def centred(value)
22
+ unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
23
+ fail InvalidSyntax, 'Argument must be `true` or `false` for centred.'
37
24
  end
38
- end
39
25
 
40
- # Use the specified interface; useful for sharing attributes with other
41
- # interfaces.
42
- #
43
- # @api public
44
- # @param value [String]
45
- # @see Vedeu::API#use
46
- def use(value)
47
- Vedeu.use(value)
26
+ attributes[:geometry][:centred] = value
48
27
  end
49
28
 
50
29
  # Define the cursor visibility for an interface. A `true` value will show
@@ -96,6 +75,54 @@ module Vedeu
96
75
  attributes[:group] = value
97
76
  end
98
77
 
78
+ # Define the number of characters/rows/lines tall the interface will be.
79
+ #
80
+ # @api public
81
+ # @param value [Fixnum]
82
+ #
83
+ # @example
84
+ # interface 'my_interface' do
85
+ # height 8
86
+ # ...
87
+ #
88
+ # @return [API::Interface]
89
+ def height(value)
90
+ Vedeu.log(out_of_bounds('height')) if y_out_of_bounds?(value)
91
+
92
+ attributes[:geometry][:height] = value
93
+ end
94
+
95
+ # Define a single line in a view.
96
+ #
97
+ # @api public
98
+ # @param value [String]
99
+ # @param block [Proc]
100
+ #
101
+ # @example
102
+ # view 'my_interface' do
103
+ # line 'This is a line of text...'
104
+ # line 'and so is this...'
105
+ # ...
106
+ #
107
+ # view 'my_interface' do
108
+ # line do
109
+ # ... some line attributes ...
110
+ # end
111
+ # end
112
+ #
113
+ # @return [API::Interface]
114
+ def line(value = '', &block)
115
+ if block_given?
116
+ attributes[:lines] << API::Line
117
+ .build({ parent: self.view_attributes }, &block)
118
+
119
+ else
120
+ attributes[:lines] << API::Line
121
+ .build({ streams: { text: value }, parent: self.view_attributes })
122
+
123
+ end
124
+ end
125
+
99
126
  # The name of the interface. Used to reference the interface throughout
100
127
  # your application's execution lifetime.
101
128
  #
@@ -112,6 +139,33 @@ module Vedeu
112
139
  attributes[:name] = value
113
140
  end
114
141
 
142
+ # Use the specified interface; useful for sharing attributes with other
143
+ # interfaces.
144
+ #
145
+ # @api public
146
+ # @param value [String]
147
+ # @see Vedeu::API#use
148
+ def use(value)
149
+ Vedeu.use(value)
150
+ end
151
+
152
+ # Define the number of characters/columns wide the interface will be.
153
+ #
154
+ # @api public
155
+ # @param value [Fixnum]
156
+ #
157
+ # @example
158
+ # interface 'my_interface' do
159
+ # width 25
160
+ # ...
161
+ #
162
+ # @return [API::Interface]
163
+ def width(value)
164
+ Vedeu.log(out_of_bounds('width')) if x_out_of_bounds?(value)
165
+
166
+ attributes[:geometry][:width] = value
167
+ end
168
+
115
169
  # Define the starting x position (column) of the interface.
116
170
  #
117
171
  # @api public
@@ -161,58 +215,32 @@ module Vedeu
161
215
  attributes[:geometry][:y] = value
162
216
  end
163
217
 
164
- # Define the number of characters/columns wide the interface will be.
165
- #
166
- # @api public
167
- # @param value [Fixnum]
168
- #
169
- # @example
170
- # interface 'my_interface' do
171
- # width 25
172
- # ...
173
- #
174
- # @return [API::Interface]
175
- def width(value)
176
- Vedeu.log(out_of_bounds('width')) if x_out_of_bounds?(value)
218
+ private
177
219
 
178
- attributes[:geometry][:width] = value
220
+ # Returns the out of bounds error message for the given named attribute.
221
+ #
222
+ # @api private
223
+ # @param name [String]
224
+ # @return [String]
225
+ def out_of_bounds(name)
226
+ "Note: For this terminal, the value of '#{name}' may lead to content " \
227
+ "that is outside the viewable area."
179
228
  end
180
229
 
181
- # Define the number of characters/rows/lines tall the interface will be.
230
+ # Checks the value is within the terminal's confines.
182
231
  #
183
- # @api public
184
- # @param value [Fixnum]
185
- #
186
- # @example
187
- # interface 'my_interface' do
188
- # height 8
189
- # ...
190
- #
191
- # @return [API::Interface]
192
- def height(value)
193
- Vedeu.log(out_of_bounds('height')) if y_out_of_bounds?(value)
194
-
195
- attributes[:geometry][:height] = value
232
+ # @api private
233
+ # @return [Boolean]
234
+ def y_out_of_bounds?(value)
235
+ value < 1 || value > Terminal.height
196
236
  end
197
237
 
198
- # Instructs Vedeu to calculate x and y geometry automatically based on the
199
- # centre character of the terminal, the width and the height.
200
- #
201
- # @api public
202
- # @param value [Boolean]
203
- #
204
- # @example
205
- # interface 'my_interface' do
206
- # centred true
207
- # ...
238
+ # Checks the value is within the terminal's confines.
208
239
  #
209
- # @return [API::Interface]
210
- def centred(value)
211
- unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
212
- fail InvalidSyntax, 'Argument must be `true` or `false` for centred.'
213
- end
214
-
215
- attributes[:geometry][:centred] = value
240
+ # @api private
241
+ # @return [Boolean]
242
+ def x_out_of_bounds?(value)
243
+ value < 1 || value > Terminal.width
216
244
  end
217
245
 
218
246
  end
@@ -0,0 +1,62 @@
1
+ module Vedeu
2
+ module API
3
+
4
+ # Provides methods to be used to define keypress mapped to actions.
5
+ class Keymap < Vedeu::Keymap
6
+
7
+ # Define keypress(es) to perform an action.
8
+ #
9
+ # @param value_or_values [String|Symbol] The key(s) pressed. Special keys
10
+ # can be found in {Vedeu::Input#specials}. When more than one key is
11
+ # defined, then the extras are treated as aliases.
12
+ # @param block [Proc] The action to perform when this key is pressed. Can
13
+ # be a method call or event triggered.
14
+ #
15
+ # @example
16
+ # keys do
17
+ # key('s') { trigger(:save) }
18
+ # key('h', :left) { trigger(:left) }
19
+ # key('j', :down) { trigger(:down) }
20
+ # ...
21
+ #
22
+ # @return [Array] A collection containing the keypress(es).
23
+ def key(*value_or_values, &block)
24
+ fail InvalidSyntax,
25
+ 'No action defined for `key`.' unless block_given?
26
+ fail InvalidSyntax, 'No keypress(es) defined for `key`.' unless
27
+ defined_value?(value_or_values)
28
+
29
+ value_or_values.each do |value|
30
+ fail InvalidSyntax, 'Key cannot be empty.' unless
31
+ defined_value?(value)
32
+
33
+ attributes[:keys] << { key: value, action: block }
34
+ end
35
+ end
36
+
37
+ # The interface(s) which will handle these keys.
38
+ #
39
+ # @param name_or_names [String] The name or names of the interface(s)
40
+ # which will handle these keys.
41
+ #
42
+ # @example
43
+ # keys do
44
+ # interface 'my_interface'
45
+ # key('s') { :something }
46
+ # name 'my_keymap'
47
+ # ...
48
+ #
49
+ # keys do
50
+ # interface('main', 'other')
51
+ # key('s') { :something }
52
+ # ...
53
+ #
54
+ # @return [Array]
55
+ def interface(*name_or_names)
56
+ attributes[:interfaces] = name_or_names
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+ end
@@ -92,8 +92,10 @@ module Vedeu
92
92
  }
93
93
  end
94
94
 
95
+ # At present, validates that a menu has a name attribute.
96
+ #
95
97
  # @api private
96
- # @return [TrueClass|FalseClass]
98
+ # @return [Boolean]
97
99
  def validate_attributes!
98
100
  unless defined_value?(attributes[:name])
99
101
  fail InvalidSyntax, 'Menus must have a `name`.'
@@ -83,7 +83,7 @@ module Vedeu
83
83
  # Returns whether debugging is enabled or disabled. Default is false;
84
84
  # meaning nothing apart from warnings are written to the log file.
85
85
  #
86
- # @return [TrueClass|FalseClass]
86
+ # @return [Boolean]
87
87
  def debug?
88
88
  options[:debug]
89
89
  end
@@ -93,7 +93,7 @@ module Vedeu
93
93
  # standalone (will run until terminates of natural causes.) Default is true;
94
94
  # meaning the application will require user input.
95
95
  #
96
- # @return [TrueClass|FalseClass]
96
+ # @return [Boolean]
97
97
  def interactive?
98
98
  options[:interactive]
99
99
  end
@@ -103,12 +103,19 @@ module Vedeu
103
103
  # not. Default is false; meaning the application will loop forever or until
104
104
  # terminated by the user.
105
105
  #
106
- # @return [TrueClass|FalseClass]
106
+ # @return [Boolean]
107
107
  def once?
108
108
  options[:once]
109
109
  end
110
110
  alias_method :once, :once?
111
111
 
112
+ # Returns
113
+ #
114
+ # @return [Hash]
115
+ def system_keys
116
+ options[:system_keys]
117
+ end
118
+
112
119
  # Returns the terminal mode for the application. Default is `:raw`.
113
120
  #
114
121
  # @return [Symbol]
@@ -120,7 +127,7 @@ module Vedeu
120
127
  # the log file (logging method calls and events trigger). Default is false;
121
128
  # meaning tracing is disabled.
122
129
  #
123
- # @return [TrueClass|FalseClass]
130
+ # @return [Boolean]
124
131
  def trace?
125
132
  options[:trace]
126
133
  end
@@ -153,11 +160,22 @@ module Vedeu
153
160
  debug: detect_debug_mode,
154
161
  interactive: true,
155
162
  once: false,
163
+ system_keys: default_system_keys,
156
164
  terminal_mode: :raw, #cooked
157
165
  trace: detect_trace_mode,
158
166
  }
159
167
  end
160
168
 
169
+ # Vedeu's system keys.
170
+ def default_system_keys
171
+ {
172
+ exit: 'q',
173
+ focus_next: :tab,
174
+ focus_prev: :shift_tab,
175
+ mode_switch: :escape,
176
+ }
177
+ end
178
+
161
179
  # Determine the terminal colour mode via enviroment variables, or be
162
180
  # optimistic and settle for 256 colours.
163
181
  #
@@ -189,7 +207,7 @@ module Vedeu
189
207
  # Determine the debug mode via an enviroment variable.
190
208
  #
191
209
  # @api private
192
- # @return [TrueClass|FalseClass]
210
+ # @return [Boolean]
193
211
  # :nocov:
194
212
  def detect_debug_mode
195
213
  if ENV['VEDEU_DEBUG']
@@ -209,7 +227,7 @@ module Vedeu
209
227
  # Determine the trace mode via an environment variable.
210
228
  #
211
229
  # @api private
212
- # @return [TrueClass|FalseClass]
230
+ # @return [Boolean]
213
231
  # :nocov:
214
232
  def detect_trace_mode
215
233
  if ENV['VEDEU_TRACE']
@@ -5,31 +5,23 @@ module Vedeu
5
5
  # @api private
6
6
  module Coercions
7
7
 
8
- # Contains class methods which are accessible as such to classes and modules
9
- # which include {Vedeu::Coercions}.
10
- module ClassMethods
11
-
12
- include Vedeu::Common
13
-
14
- # Produces new objects of the correct class from attributes hashes,
15
- # ignores objects that have already been coerced.
16
- # When provided with a parent argument, will allow the new object
17
- # to know the colour and style of its parent.
18
- #
19
- # @param values [Array|Hash]
20
- # @param parent [Hash|Nil]
21
- # @return [Array]
22
- def coercer(values, parent = nil)
23
- return [] unless defined_value?(values)
24
-
25
- [values].flatten.map do |value|
26
- if value.is_a?(self)
27
- value
28
-
29
- else
30
- self.new(value.merge!({ parent: parent }))
31
-
32
- end
8
+ include Vedeu::Common
9
+
10
+ # Produces new objects of the correct class from attributes hashes,
11
+ # ignores objects that have already been coerced.
12
+ #
13
+ # @param values [Array|Hash]
14
+ # @return [Array]
15
+ def coercer(values)
16
+ return [] unless defined_value?(values)
17
+
18
+ [values].flatten.map do |value|
19
+ if value.is_a?(self)
20
+ value
21
+
22
+ else
23
+ self.new(value)
24
+
33
25
  end
34
26
  end
35
27
  end
@@ -38,7 +30,7 @@ module Vedeu
38
30
  # module, make its methods into class methods, so they may be called
39
31
  # directly.
40
32
  def self.included(receiver)
41
- receiver.extend(ClassMethods)
33
+ receiver.extend(self)
42
34
  end
43
35
 
44
36
  end