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
@@ -0,0 +1,239 @@
1
+ module Vedeu
2
+
3
+ # Repository for storing, retrieving and using defined keymaps.
4
+ #
5
+ # @api private
6
+ module Keymaps
7
+
8
+ include Vedeu::Common
9
+ extend self
10
+
11
+ # Stores the keymap attributes defined by the API.
12
+ #
13
+ # @param attributes [Hash]
14
+ # @return [TrueClass|KeyInUse|FalseClass]
15
+ def add(attributes)
16
+ return false unless defined_value?(attributes[:keys])
17
+
18
+ if defined_value?(attributes[:interfaces])
19
+ attributes[:interfaces].map do |interface|
20
+ storage.store(interface, {}) unless registered?(interface)
21
+
22
+ register(attributes, interface)
23
+ end
24
+
25
+ else
26
+ register(attributes)
27
+
28
+ end
29
+
30
+ true
31
+ end
32
+
33
+ # Return the whole repository of keymaps.
34
+ #
35
+ # @return [Hash]
36
+ def all
37
+ storage
38
+ end
39
+
40
+ # Find a keymap by interface name.
41
+ #
42
+ # @param name [String]
43
+ # @return [Hash]
44
+ def find(name)
45
+ storage.fetch(name, {})
46
+ end
47
+
48
+ # Return a boolean indicating whether the key is registered as a global key.
49
+ #
50
+ # @param key [String|Symbol]
51
+ # @return [Boolean]
52
+ def global_key?(key)
53
+ global_keys.include?(key)
54
+ end
55
+
56
+ # Return the collection of global keys.
57
+ #
58
+ # @return [Array]
59
+ def global_keys
60
+ storage.fetch('_global_keymap_', {}).keys
61
+ end
62
+
63
+ # Return a boolean indicating whether the key is registered as an interface
64
+ # key. When an interface argument is provided, only that interface is
65
+ # checked.
66
+ #
67
+ # @param key [String|Symbol]
68
+ # @param interface [String]
69
+ # @return [Boolean]
70
+ def interface_key?(key, interface = '')
71
+ if defined_value?(interface)
72
+ find(interface).keys.include?(key)
73
+
74
+ else
75
+ interface_keys.include?(key)
76
+
77
+ end
78
+ end
79
+
80
+ # Return a collection of interface keys.
81
+ #
82
+ # @return [Hash]
83
+ def interface_keys
84
+ storage.reject do |k, _|
85
+ k == '_global_keymap_'
86
+ end.map { |_, v| v.keys }.flatten.uniq
87
+ end
88
+
89
+ # Returns a collection of the interface names of all the registered keymaps.
90
+ #
91
+ # @return [Array]
92
+ def registered
93
+ storage.keys
94
+ end
95
+
96
+ # Returns a boolean indicating whether the named interface has a keymap
97
+ # registered.
98
+ #
99
+ # @param name [String]
100
+ # @return [Boolean]
101
+ def registered?(name)
102
+ storage.key?(name)
103
+ end
104
+
105
+ # Reset the keymaps repository; removing all registered keymaps. Only the
106
+ # system keymap will remain.
107
+ #
108
+ # @return [Hash]
109
+ def reset
110
+ @_storage = in_memory
111
+ end
112
+
113
+ # Return a boolean indicating whether the key is registered as a system key.
114
+ #
115
+ # @param key [String|Symbol]
116
+ # @return [Boolean]
117
+ def system_key?(key)
118
+ system_keys.include?(key)
119
+ end
120
+
121
+ # Return a collection of system keys.
122
+ #
123
+ # @return [Array]
124
+ def system_keys
125
+ Configuration.system_keys.invert.keys
126
+ end
127
+
128
+ # Handles the keypress in your application. Can also be used to simulate a
129
+ # keypress.
130
+ #
131
+ # 1) Log the keypress if debugging is enabled.
132
+ # 2) Trigger the client application's `:key` event (it may not exist).
133
+ # 3) Determine if the key pertains to the focussed interface and action it,
134
+ # or check both global, then system keys to action it. Returns false if
135
+ # nothing can deal with it.
136
+ #
137
+ # @param key [String|Symbol] The key which was pressed. Escape sequences
138
+ # are also supported. Special keys like the F-keys are named as symbols;
139
+ # i.e. `:f4`. A list of these translations can be found at {Vedeu::Input}.
140
+ #
141
+ # @example
142
+ # Vedeu.keypress('s')
143
+ #
144
+ # @return [|FalseClass]
145
+ def use(key)
146
+ Vedeu.log("Key pressed: '#{key}'")
147
+
148
+ Vedeu.trigger(:key, key)
149
+
150
+ focussed_interface = Vedeu::Focus.current
151
+
152
+ if interface_key?(key, focussed_interface)
153
+ find(focussed_interface).fetch(key, noop).call
154
+
155
+ elsif global_key?(key)
156
+ find('_global_keymap_').fetch(key, noop).call
157
+
158
+ elsif system_key?(key)
159
+ system_key(key)
160
+
161
+ else
162
+ false
163
+
164
+ end
165
+ end
166
+
167
+ private
168
+
169
+ # Triggers the system event defined for this key.
170
+ #
171
+ # @param key [String|Symbol]
172
+ # @return []
173
+ def system_key(key)
174
+ action = Vedeu::Configuration.system_keys.key(key)
175
+ event = ['_', action, '_'].join.to_sym
176
+
177
+ Vedeu.trigger(event)
178
+ end
179
+
180
+ # @param key [String|Symbol]
181
+ # @param interface [String]
182
+ # @return []
183
+ def validate(key, interface = '')
184
+ Vedeu::KeymapValidator.check(storage, key, interface)
185
+ end
186
+
187
+ # Registers the key.
188
+ #
189
+ # @api private
190
+ # @param attributes [Hash]
191
+ # @param interface [String]
192
+ # @return []
193
+ def register(attributes, interface = '')
194
+ attributes[:keys].map do |keymap|
195
+ valid, message = validate(keymap[:key], interface)
196
+
197
+ fail KeyInUse, message unless valid
198
+
199
+ Vedeu.log("Registering key '#{keymap[:key]}' with " \
200
+ "'#{namespace(interface)}'")
201
+
202
+ storage[namespace(interface)]
203
+ .merge!({ keymap[:key] => keymap[:action] })
204
+ end
205
+ end
206
+
207
+ # Determine which interface to store the key with.
208
+ #
209
+ # @api private
210
+ # @param interface [String]
211
+ # @return [String]
212
+ def namespace(interface = '')
213
+ return defined_value?(interface) ? interface : '_global_keymap_'
214
+ end
215
+
216
+ # Returns a noop proc which when called returns :noop.
217
+ #
218
+ # @return [Proc]
219
+ def noop
220
+ proc { :noop }
221
+ end
222
+
223
+ # Access to the storage for this repository.
224
+ #
225
+ # @api private
226
+ # @return [Array]
227
+ def storage
228
+ @_storage ||= in_memory
229
+ end
230
+
231
+ # @api private
232
+ # @return [Array]
233
+ def in_memory
234
+ { '_global_keymap_' => {} }
235
+ end
236
+
237
+ end
238
+
239
+ end
@@ -5,6 +5,7 @@ module Vedeu
5
5
  # @api private
6
6
  module Menus
7
7
 
8
+ include Common
8
9
  extend self
9
10
 
10
11
  # System events which when called with the appropriate menu name will
@@ -25,7 +26,9 @@ module Vedeu
25
26
  # @param attributes [Hash]
26
27
  # @return [Hash|FalseClass]
27
28
  def add(attributes)
28
- return false if attributes[:name].empty?
29
+ return false unless defined_value?(attributes[:name])
30
+
31
+ Vedeu.log("Registering menu '#{attributes[:name]}'")
29
32
 
30
33
  storage.store(attributes[:name], attributes)
31
34
  end
@@ -57,7 +60,8 @@ module Vedeu
57
60
 
58
61
  # Returns a boolean indicating whether the named menu is registered.
59
62
  #
60
- # @return [TrueClass|FalseClass]
63
+ # @param name [String]
64
+ # @return [Boolean]
61
65
  def registered?(name)
62
66
  storage.key?(name)
63
67
  end
@@ -65,7 +69,7 @@ module Vedeu
65
69
  # Removes the menu from the repository and associated events.
66
70
  #
67
71
  # @param name [String]
68
- # @return [TrueClass|FalseClass]
72
+ # @return [Boolean]
69
73
  def remove(name)
70
74
  return false unless registered?(name)
71
75
 
@@ -95,12 +99,17 @@ module Vedeu
95
99
 
96
100
  private
97
101
 
102
+ # Access to the storage for this repository.
103
+ #
98
104
  # @api private
99
105
  # @return [Hash]
100
106
  def storage
101
107
  @_storage ||= in_memory
102
108
  end
103
109
 
110
+ # Returns an empty collection ready for the storing of menus by name with
111
+ # associated menu instance.
112
+ #
104
113
  # @api private
105
114
  # @return [Hash]
106
115
  def in_memory
@@ -7,8 +7,8 @@ module Vedeu
7
7
 
8
8
  # Returns a boolean indicating whether a variable has a useful value.
9
9
  #
10
- # @param variable [String|Array] The variable to check.
11
- # @return [TrueClass|FalseClass]
10
+ # @param variable [String|Symbol|Array] The variable to check.
11
+ # @return [Boolean]
12
12
  def defined_value?(variable)
13
13
  return true unless variable.nil? || variable.empty?
14
14
 
@@ -5,6 +5,9 @@ module Vedeu
5
5
  # @api private
6
6
  class Cursor
7
7
 
8
+ # @return [Hash]
9
+ attr_reader :attributes
10
+
8
11
  # @return [Fixnum]
9
12
  attr_reader :top
10
13
 
@@ -25,9 +25,9 @@ module Vedeu
25
25
  def trigger(*args)
26
26
  return execute(*args) unless debouncing? || throttling?
27
27
 
28
- return execute(*args) if debouncing? && set_executed > deadline
28
+ return execute(*args) if debouncing? && debounce_expired?
29
29
 
30
- return execute(*args) if throttling? && elapsed_time > delay
30
+ return execute(*args) if throttling? && throttle_expired?
31
31
  end
32
32
 
33
33
  private
@@ -63,19 +63,36 @@ module Vedeu
63
63
  # throttling.
64
64
  #
65
65
  # @api private
66
- # @return [TrueClass|FalseClass]
66
+ # @return [Boolean]
67
67
  def throttling?
68
68
  set_time
69
69
 
70
70
  options[:delay] > 0
71
71
  end
72
72
 
73
+ # Returns a boolean indicating whether the throttle has expired.
74
+ #
75
+ # @api private
76
+ # @return [Boolean]
77
+ def throttle_expired?
78
+ if elapsed_time > delay
79
+ Vedeu.log("Event throttle has expired for '#{event_name}', executing " \
80
+ "event.")
81
+ true
82
+
83
+ else
84
+ Vedeu.log("Event throttle not yet expired for '#{event_name}'.")
85
+ false
86
+
87
+ end
88
+ end
89
+
73
90
  # Returns a boolean indicating whether debouncing is required for this
74
91
  # event. Setting the debounce option to any value greater than 0 will
75
92
  # enable debouncing.
76
93
  #
77
94
  # @api private
78
- # @return [TrueClass|FalseClass]
95
+ # @return [Boolean]
79
96
  def debouncing?
80
97
  set_time
81
98
 
@@ -84,6 +101,23 @@ module Vedeu
84
101
  options[:debounce] > 0
85
102
  end
86
103
 
104
+ # Returns a boolean indicating whether the debounce has expired.
105
+ #
106
+ # @api private
107
+ # @return [Boolean]
108
+ def debounce_expired?
109
+ if set_executed > deadline
110
+ Vedeu.log("Event debounce has expired for '#{event_name}', executing " \
111
+ "event.")
112
+ true
113
+
114
+ else
115
+ Vedeu.log("Event debounce not yet expired for '#{event_name}'.")
116
+ false
117
+
118
+ end
119
+ end
120
+
87
121
  # @api private
88
122
  # @return [Float]
89
123
  def elapsed_time
@@ -109,7 +143,7 @@ module Vedeu
109
143
  end
110
144
 
111
145
  # @api private
112
- # @return [TrueClass|FalseClass]
146
+ # @return [Boolean]
113
147
  def has_deadline?
114
148
  @deadline > 0
115
149
  end
@@ -128,6 +162,12 @@ module Vedeu
128
162
  nil
129
163
  end
130
164
 
165
+ # @api private
166
+ # @return [String]
167
+ def event_name
168
+ options[:event_name].to_s
169
+ end
170
+
131
171
  # @api private
132
172
  # @return [Fixnum|Float]
133
173
  def debounce
@@ -150,8 +190,9 @@ module Vedeu
150
190
  # @return [Hash]
151
191
  def defaults
152
192
  {
153
- delay: 0.0,
154
- debounce: 0.0
193
+ delay: 0.0,
194
+ debounce: 0.0,
195
+ event_name: '',
155
196
  }
156
197
  end
157
198
 
@@ -63,7 +63,7 @@ module Vedeu
63
63
  end
64
64
 
65
65
  # @api private
66
- # @return [TrueClass|FalseClass]
66
+ # @return [Boolean]
67
67
  def out_of_range?
68
68
  value < 1 || value > 12
69
69
  end
@@ -0,0 +1,66 @@
1
+ module Vedeu
2
+
3
+ # When the client application has defined interfaces to be used, the Registrar
4
+ # stores these interfaces into various repositories for later use.
5
+ #
6
+ # @api private
7
+ class Registrar
8
+
9
+ include Common
10
+
11
+ # @param attributes [Hash]
12
+ # @return [TrueClass|]
13
+ def self.record(attributes = {})
14
+ new(attributes).record
15
+ end
16
+
17
+ # @param attributes [Hash]
18
+ # @return [Registrar]
19
+ def initialize(attributes = {})
20
+ @attributes = attributes
21
+ end
22
+
23
+ # Adds the attributes to a variety of repositories to use later.
24
+ #
25
+ # @return [TrueClass|MissingRequired]
26
+ def record
27
+ validate_attributes!
28
+
29
+ Vedeu::Buffers.add(attributes)
30
+
31
+ Vedeu::Interfaces.add(attributes)
32
+
33
+ Vedeu::Groups.add(attributes)
34
+
35
+ Vedeu::Focus.add(attributes)
36
+
37
+ true
38
+ end
39
+
40
+ private
41
+
42
+ # Client application defined settings for interfaces etc.
43
+ attr_reader :attributes
44
+
45
+ # At present, validates that attributes has a `:name` key that is not nil or
46
+ # empty.
47
+ #
48
+ # @api private
49
+ # @return [TrueClass|MissingRequired]
50
+ def validate_attributes!
51
+ return exception unless attributes.key?(:name)
52
+ return exception unless defined_value?(attributes[:name])
53
+
54
+ true
55
+ end
56
+
57
+ # Raises the MissingRequired exception.
58
+ #
59
+ # @see Vedeu::MissingRequired
60
+ def exception
61
+ fail MissingRequired, 'Cannot store data without a name attribute.'
62
+ end
63
+
64
+ end
65
+
66
+ end