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
@@ -50,7 +50,10 @@ module Vedeu
50
50
 
51
51
  # The client application may have created a line that us too long for the
52
52
  # interface. This code tries to truncate streams whilst preserving styles
53
- # and colours.
53
+ # and colours. To achieve this, it successively checks each stream length
54
+ # against remaining line length and truncates the stream data if it
55
+ # exceeds the line length. Further stream data that does not fit is
56
+ # discarded.
54
57
  #
55
58
  # @api private
56
59
  # @return [Array]
@@ -60,32 +63,24 @@ module Vedeu
60
63
  lines.map do |line|
61
64
  if exceeds_width?(line)
62
65
  line_length = 0
63
- processed = []
64
- line.streams.each do |stream|
66
+ new_streams = []
67
+
68
+ new_streams = line.streams.map do |stream|
65
69
  next if stream.text.empty?
66
70
 
67
71
  if (line_length += stream.text.size) >= width
68
72
  remainder = width - line_length
73
+ truncated = truncate(stream.text, remainder)
69
74
 
70
- processed << Stream.new({
71
- colour: stream.colour.attributes,
72
- style: stream.style.values,
73
- text: truncate(stream.text, remainder),
74
- parent: line.view_attributes,
75
- })
75
+ build_stream(line, stream, truncated)
76
76
 
77
77
  else
78
- processed << stream
78
+ stream
79
79
 
80
80
  end
81
81
  end
82
82
 
83
- Line.new({
84
- colour: line.colour.attributes,
85
- streams: processed,
86
- style: line.style.values,
87
- parent: interface.view_attributes,
88
- })
83
+ build_line(line, new_streams)
89
84
 
90
85
  else
91
86
  line
@@ -94,13 +89,45 @@ module Vedeu
94
89
  end
95
90
  end
96
91
 
92
+ # Builds a new Stream object with the newly truncated text and previous
93
+ # attributes.
94
+ #
95
+ # @api private
96
+ # @param line [Line]
97
+ # @param stream [Stream]
98
+ # @param text [String]
99
+ # @return [Stream]
100
+ def build_stream(line, stream, text)
101
+ attributes = stream.view_attributes.merge!({
102
+ parent: line.view_attributes,
103
+ text: text,
104
+ })
105
+
106
+ Stream.new(attributes)
107
+ end
108
+
109
+ # Builds a new Line object with the new streams and previous attributes.
110
+ #
111
+ # @api private
112
+ # @param line [Line]
113
+ # @param streams [Array]
114
+ # @return [Line]
115
+ def build_line(line, streams)
116
+ attributes = line.view_attributes.merge!({
117
+ parent: interface.view_attributes,
118
+ streams: streams,
119
+ })
120
+
121
+ Line.new(attributes)
122
+ end
123
+
97
124
  # Converts all streams within a line into a single line of text to then
98
125
  # check that this line (without formatting, as that is not visible) exceeds
99
126
  # the width of the interface.
100
127
  #
101
128
  # @api private
102
129
  # @param line [Line]
103
- # @return [TrueClass|FalseClass]
130
+ # @return [Boolean]
104
131
  def exceeds_width?(line)
105
132
  line.streams.map(&:text).join.size > width
106
133
  end
@@ -4,6 +4,9 @@ module Vedeu
4
4
  # This method should contain attributes required to build a view or views.
5
5
  # These attributes will be added to the back buffer of each interface
6
6
  # mentioned, to be rendered upon next refresh.
7
+ #
8
+ # @deprecated May disappear in 0.3.0. Prefer {Vedeu::API#render} instead.
9
+ # @see Vedeu::API#render
7
10
  class View
8
11
 
9
12
  include Vedeu::API
@@ -9,28 +9,16 @@ module Vedeu
9
9
  include Vedeu::Common
10
10
  extend self
11
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
12
  # Add an interface view into the back buffer. If the buffer is already
27
- # registered, then we preserve its front buffer.
13
+ # registered, then we preserve its front buffer. Returns the name of the
14
+ # buffer added to storage.
28
15
  #
29
16
  # @param attributes [Hash]
30
- # @return [Hash]
17
+ # @return [String]
31
18
  def add(attributes)
32
19
  if registered?(attributes[:name])
33
20
  buffer = find(attributes[:name])
21
+
34
22
  buffer[:back_buffer] = attributes
35
23
 
36
24
  else
@@ -41,7 +29,7 @@ module Vedeu
41
29
 
42
30
  end
43
31
 
44
- storage
32
+ attributes[:name]
45
33
  end
46
34
 
47
35
  # Find the buffer by name.
@@ -72,10 +60,10 @@ module Vedeu
72
60
  def latest(name)
73
61
  if new_content?(name)
74
62
  swap_buffers(name)
75
- content(name)
63
+ front_buffer(name)
76
64
 
77
65
  elsif old_content?(name)
78
- content(name)
66
+ front_buffer(name)
79
67
 
80
68
  else
81
69
  nil
@@ -83,16 +71,35 @@ module Vedeu
83
71
  end
84
72
  end
85
73
 
86
- # Returns the named front buffer.
74
+ # Returns a collection of the names of all registered buffers.
75
+ #
76
+ # @return [Array]
77
+ def registered
78
+ storage.keys
79
+ end
80
+
81
+ # Returns a boolean indicating whether the named buffer is registered.
87
82
  #
83
+ # @api private
88
84
  # @param name [String]
85
+ # @return [Boolean]
86
+ def registered?(name)
87
+ storage.key?(name)
88
+ end
89
+
90
+ # Reset the buffers repository; removing all buffers. This does not delete
91
+ # the interfaces themselves.
92
+ #
89
93
  # @return [Hash]
90
- def content(name)
91
- front_buffer(name)
94
+ def reset
95
+ @_storage = in_memory
92
96
  end
93
97
 
98
+ private
99
+
94
100
  # Swap the named back buffer into the front buffer of the same name.
95
101
  #
102
+ # @api private
96
103
  # @param name [String]
97
104
  # @return [Hash]
98
105
  def swap_buffers(name)
@@ -104,21 +111,11 @@ module Vedeu
104
111
  })
105
112
  end
106
113
 
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
114
  # Return a boolean indicating whether the named back buffer has new content.
118
115
  #
119
116
  # @api private
120
117
  # @param name [String]
121
- # @return [TrueClass|FalseClass]
118
+ # @return [Boolean]
122
119
  def new_content?(name)
123
120
  defined_value?(back_buffer(name))
124
121
  end
@@ -127,7 +124,7 @@ module Vedeu
127
124
  #
128
125
  # @api private
129
126
  # @param name [String]
130
- # @return [TrueClass|FalseClass]
127
+ # @return [Boolean]
131
128
  def old_content?(name)
132
129
  defined_value?(front_buffer(name))
133
130
  end
@@ -150,15 +147,8 @@ module Vedeu
150
147
  find(name).fetch(:front_buffer, nil)
151
148
  end
152
149
 
153
- # Returns a boolean indicating whether the named buffer is registered.
150
+ # Access to the storage for this repository.
154
151
  #
155
- # @api private
156
- # @param name [String]
157
- # @return [TrueClass|FalseClass]
158
- def registered?(name)
159
- storage.key?(name)
160
- end
161
-
162
152
  # @api private
163
153
  # @return [Hash]
164
154
  def storage
@@ -16,18 +16,13 @@ module Vedeu
16
16
  instance_eval(&block) if block_given?
17
17
  end
18
18
 
19
- # @param object []
20
- # @param block [Proc]
21
- # @return []
22
- def add(object, &block)
23
- @self_before_instance_eval = eval('self', block.binding)
24
-
25
- instance_eval(&block)
26
- end
27
-
28
19
  # @see Vedeu::API#event
29
20
  def event(name, opts = {}, &block)
30
- handlers[name][:events] << Event.new(block, opts)
21
+ Vedeu.log("Registering event '#{name}'")
22
+
23
+ options = opts.merge!({ event_name: name })
24
+
25
+ handlers[name][:events] << Event.new(block, options)
31
26
  handlers[name]
32
27
  end
33
28
 
@@ -45,7 +40,9 @@ module Vedeu
45
40
 
46
41
  # Returns a Boolean indicating whether the named event is registered.
47
42
  #
48
- # @return [TrueClass|FalseClass]
43
+ # @api private
44
+ # @param name [Symbol] The name of the event to check.
45
+ # @return [Boolean]
49
46
  def registered?(name)
50
47
  handlers.key?(name)
51
48
  end
@@ -41,6 +41,8 @@ module Vedeu
41
41
  def current
42
42
  fail NoInterfacesDefined if storage.empty?
43
43
 
44
+ Vedeu.log("Interface in focus: '#{storage.first}'")
45
+
44
46
  storage.first
45
47
  end
46
48
 
@@ -69,32 +71,32 @@ module Vedeu
69
71
  storage
70
72
  end
71
73
 
72
- # Reset the focus repository; removing all items. This does not delete
73
- # the interfaces themselves.
74
- #
75
- # @return [Hash]
76
- def reset
77
- @storage = in_memory
78
- end
79
-
80
- private
81
-
82
74
  # Returns a boolean indicating whether the named interface is registered.
83
75
  #
84
76
  # @api private
85
- # @return [TrueClass|FalseClass]
77
+ # @return [Boolean]
86
78
  def registered?(name)
87
79
  return false if storage.empty?
88
80
 
89
81
  storage.include?(name)
90
82
  end
91
83
 
92
- # Provides accessor to the in-memory storage.
84
+ # Reset the focus repository; removing all items. This does not delete
85
+ # the interfaces themselves.
86
+ #
87
+ # @return [Hash]
88
+ def reset
89
+ @_storage = in_memory
90
+ end
91
+
92
+ private
93
+
94
+ # Access to the storage for this repository.
93
95
  #
94
96
  # @api private
95
97
  # @return [Array]
96
98
  def storage
97
- @storage ||= in_memory
99
+ @_storage ||= in_memory
98
100
  end
99
101
 
100
102
  # Returns an empty collection ready for the storing of interface names.
@@ -5,6 +5,7 @@ module Vedeu
5
5
  # @api private
6
6
  module Groups
7
7
 
8
+ include Vedeu::Common
8
9
  extend self
9
10
 
10
11
  # Add an interface name to a group, creating the group if it doesn't already
@@ -13,9 +14,13 @@ module Vedeu
13
14
  # @param attributes [Hash]
14
15
  # @return [Groups|FalseClass]
15
16
  def add(attributes)
16
- return false if attributes[:group].empty?
17
+ return false unless defined_value?(attributes[:group])
17
18
 
18
19
  storage[attributes[:group]] << attributes[:name]
20
+
21
+ register_event(attributes)
22
+
23
+ true
19
24
  end
20
25
 
21
26
  # Return the whole repository.
@@ -45,7 +50,7 @@ module Vedeu
45
50
 
46
51
  # Returns a Boolean indicating whether the named group is registered.
47
52
  #
48
- # @return [TrueClass|FalseClass]
53
+ # @return [Boolean]
49
54
  def registered?(name)
50
55
  storage.key?(name)
51
56
  end
@@ -60,6 +65,19 @@ module Vedeu
60
65
 
61
66
  private
62
67
 
68
+ # @see Vedeu::Refresh.register_event
69
+ # @api private
70
+ # @param attributes [Hash]
71
+ # @return [Boolean]
72
+ def register_event(attributes)
73
+ name = attributes[:group]
74
+ delay = attributes[:delay] || 0.0
75
+
76
+ Vedeu::Refresh.register_event(:by_group, name, delay)
77
+ end
78
+
79
+ # Access to the storage for this repository.
80
+ #
63
81
  # @api private
64
82
  # @return [Hash]
65
83
  def storage
@@ -5,6 +5,7 @@ module Vedeu
5
5
  # @api private
6
6
  module Interfaces
7
7
 
8
+ include Vedeu::Common
8
9
  extend self
9
10
 
10
11
  # Stores the interface attributes defined by the API.
@@ -12,9 +13,15 @@ module Vedeu
12
13
  # @param attributes [Hash]
13
14
  # @return [Hash|FalseClass]
14
15
  def add(attributes)
15
- return false if attributes[:name].empty?
16
+ return false unless defined_value?(attributes[:name])
17
+
18
+ Vedeu.log("Registering interface '#{attributes[:name]}'")
16
19
 
17
20
  storage.store(attributes[:name], attributes)
21
+
22
+ register_event(attributes)
23
+
24
+ true
18
25
  end
19
26
 
20
27
  # Return the whole repository.
@@ -44,7 +51,7 @@ module Vedeu
44
51
 
45
52
  # Returns a boolean indicating whether the named interface is registered.
46
53
  #
47
- # @return [TrueClass|FalseClass]
54
+ # @return [Boolean]
48
55
  def registered?(name)
49
56
  storage.key?(name)
50
57
  end
@@ -60,6 +67,19 @@ module Vedeu
60
67
 
61
68
  private
62
69
 
70
+ # @see Vedeu::Refresh.register_event
71
+ # @api private
72
+ # @param attributes [Hash]
73
+ # @return [Boolean]
74
+ def register_event(attributes)
75
+ name = attributes[:name]
76
+ delay = attributes[:delay] || 0.0
77
+
78
+ Vedeu::Refresh.register_event(:by_name, name, delay)
79
+ end
80
+
81
+ # Access to the storage for this repository.
82
+ #
63
83
  # @api private
64
84
  # @return [Hash]
65
85
  def storage
@@ -0,0 +1,104 @@
1
+ module Vedeu
2
+
3
+ # Validates that a given key is can be used and is not already in-use, either
4
+ # by the same interface, globally or as a system key.
5
+ #
6
+ class KeymapValidator
7
+
8
+ include Common
9
+
10
+ # Checks the key is not in use by the system, is not already defined as a
11
+ # global key, is not already defined for the interface specified.
12
+ #
13
+ # @param storage [Hash]
14
+ # @param key [String|Symbol]
15
+ # @param interface [String]
16
+ # @return [Array] A boolean indicating validity, and a helpful message.
17
+ def self.check(storage, key, interface = '')
18
+ new(storage, key, interface).check
19
+ end
20
+
21
+ # Instantiates a new KeymapValidator instance.
22
+ #
23
+ # @param storage [Hash]
24
+ # @param key [String|Symbol]
25
+ # @param interface [String]
26
+ # @return [KeymapValidator]
27
+ def initialize(storage, key, interface = '')
28
+ @storage, @key, @interface = storage, key, interface
29
+ end
30
+
31
+ # @see KeymapValidator.check
32
+ def check
33
+ if system_key?(key)
34
+ [false, "#{fail_message(key)} by the system."]
35
+
36
+ elsif global_key?(key)
37
+ [false, "#{fail_message(key)} as a global key."]
38
+
39
+ elsif interface_key?(key, interface)
40
+ if defined_value?(interface)
41
+ [false, "#{fail_message(key)} by this interface."]
42
+
43
+ else
44
+ [false, "#{fail_message(key)} by another interface and therefore " \
45
+ 'cannot be global.']
46
+
47
+ end
48
+ else
49
+ [true, 'Key can be registered.']
50
+
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ attr_reader :storage, :key, :interface
57
+
58
+ # Returns a boolean indicating whether the key has been registered for the
59
+ # named interface's keymap. If no interface is specified, then it is
60
+ # assumed that the key is attempting to be registered globally, and so all
61
+ # interfaces are checked for this key. (A key cannot be global if an
62
+ # interface is using it.)
63
+ #
64
+ # @param key [String|Symbol]
65
+ # @param interface [String]
66
+ # @return [Boolean]
67
+ def interface_key?(key, interface = '')
68
+ if defined_value?(interface)
69
+ return false unless storage.key?(interface)
70
+
71
+ storage.fetch(interface).keys.include?(key)
72
+
73
+ else
74
+ storage.keys.reject do |keymap|
75
+ keymap == '_global_keymap_'
76
+ end.map { |name| storage[name].keys.include?(key) }.any?
77
+
78
+ end
79
+ end
80
+
81
+ # Returns a boolean indicating whether the key is in the global keymap.
82
+ #
83
+ # @param key [String|Symbol]
84
+ # @return [Boolean]
85
+ def global_key?(key)
86
+ storage.fetch('_global_keymap_').keys.include?(key)
87
+ end
88
+
89
+ # Returns a boolean indicating whether the key is in the system keymap.
90
+ # At present, it is not possible to redefine system keys.
91
+ #
92
+ # @param key [String|Symbol]
93
+ # @return [Boolean]
94
+ def system_key?(key)
95
+ Configuration.system_keys.values.include?(key)
96
+ end
97
+
98
+ def fail_message(key)
99
+ "Cannot register key '#{key.to_s}' as already in use"
100
+ end
101
+
102
+ end
103
+
104
+ end