vedeu 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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