whirled_peas 0.4.1 → 0.8.0

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 (238) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +1 -0
  4. data/CHANGELOG.md +37 -0
  5. data/README.md +529 -156
  6. data/Rakefile +9 -3
  7. data/bin/reset_cursor +11 -0
  8. data/bin/screen_test +68 -0
  9. data/examples/intro.rb +52 -0
  10. data/examples/scrolling.rb +54 -0
  11. data/lib/whirled_peas.rb +6 -12
  12. data/lib/whirled_peas/animator.rb +5 -0
  13. data/lib/whirled_peas/animator/debug_consumer.rb +17 -0
  14. data/lib/whirled_peas/animator/easing.rb +72 -0
  15. data/lib/whirled_peas/animator/frame.rb +5 -0
  16. data/lib/whirled_peas/animator/frameset.rb +33 -0
  17. data/lib/whirled_peas/animator/producer.rb +35 -0
  18. data/lib/whirled_peas/animator/renderer_consumer.rb +31 -0
  19. data/lib/whirled_peas/command.rb +5 -0
  20. data/lib/whirled_peas/command/base.rb +86 -0
  21. data/lib/whirled_peas/command/config_command.rb +44 -0
  22. data/lib/whirled_peas/command/debug.rb +21 -0
  23. data/lib/whirled_peas/command/fonts.rb +22 -0
  24. data/lib/whirled_peas/command/frame_command.rb +34 -0
  25. data/lib/whirled_peas/command/frames.rb +24 -0
  26. data/lib/whirled_peas/command/help.rb +38 -0
  27. data/lib/whirled_peas/command/play.rb +108 -0
  28. data/lib/whirled_peas/command/record.rb +57 -0
  29. data/lib/whirled_peas/command/still.rb +29 -0
  30. data/lib/whirled_peas/command_line.rb +23 -228
  31. data/lib/whirled_peas/config.rb +56 -6
  32. data/lib/whirled_peas/device.rb +5 -0
  33. data/lib/whirled_peas/device/null_device.rb +8 -0
  34. data/lib/whirled_peas/device/output_file.rb +19 -0
  35. data/lib/whirled_peas/device/screen.rb +26 -0
  36. data/lib/whirled_peas/errors.rb +2 -0
  37. data/lib/whirled_peas/graphics.rb +19 -0
  38. data/lib/whirled_peas/graphics/box_painter.rb +101 -0
  39. data/lib/whirled_peas/graphics/canvas.rb +118 -0
  40. data/lib/whirled_peas/graphics/composer.rb +80 -0
  41. data/lib/whirled_peas/graphics/container_coords.rb +72 -0
  42. data/lib/whirled_peas/graphics/container_dimensions.rb +93 -0
  43. data/lib/whirled_peas/graphics/container_painter.rb +363 -0
  44. data/lib/whirled_peas/graphics/debugger.rb +52 -0
  45. data/lib/whirled_peas/graphics/grid_painter.rb +69 -0
  46. data/lib/whirled_peas/graphics/mock_screen.rb +26 -0
  47. data/lib/whirled_peas/graphics/painter.rb +33 -0
  48. data/lib/whirled_peas/graphics/renderer.rb +32 -0
  49. data/lib/whirled_peas/graphics/text_dimensions.rb +15 -0
  50. data/lib/whirled_peas/graphics/text_painter.rb +40 -0
  51. data/lib/whirled_peas/settings.rb +5 -0
  52. data/lib/whirled_peas/settings/alignment.rb +24 -0
  53. data/lib/whirled_peas/settings/bg_color.rb +24 -0
  54. data/lib/whirled_peas/settings/border.rb +101 -0
  55. data/lib/whirled_peas/settings/box_settings.rb +8 -0
  56. data/lib/whirled_peas/settings/color.rb +68 -0
  57. data/lib/whirled_peas/settings/container_settings.rb +223 -0
  58. data/lib/whirled_peas/settings/debugger.rb +96 -0
  59. data/lib/whirled_peas/settings/display_flow.rb +27 -0
  60. data/lib/whirled_peas/settings/element_settings.rb +61 -0
  61. data/lib/whirled_peas/settings/grid_settings.rb +19 -0
  62. data/lib/whirled_peas/settings/margin.rb +8 -0
  63. data/lib/whirled_peas/settings/padding.rb +8 -0
  64. data/lib/whirled_peas/settings/position.rb +15 -0
  65. data/lib/whirled_peas/settings/scrollbar.rb +15 -0
  66. data/lib/whirled_peas/settings/sizing.rb +19 -0
  67. data/lib/whirled_peas/settings/spacing.rb +58 -0
  68. data/lib/whirled_peas/settings/text_color.rb +21 -0
  69. data/lib/whirled_peas/settings/text_settings.rb +15 -0
  70. data/lib/whirled_peas/settings/vert_alignment.rb +24 -0
  71. data/lib/whirled_peas/utils/ansi.rb +19 -56
  72. data/lib/whirled_peas/utils/file_handler.rb +57 -0
  73. data/lib/whirled_peas/utils/formatted_string.rb +64 -0
  74. data/lib/whirled_peas/utils/title_font.rb +1 -1
  75. data/lib/whirled_peas/version.rb +1 -1
  76. data/screen_test/elements/box.frame +1 -0
  77. data/screen_test/elements/box.rb +20 -0
  78. data/screen_test/elements/grid.frame +1 -0
  79. data/screen_test/elements/grid.rb +13 -0
  80. data/screen_test/elements/screen_overflow_x.frame +1 -0
  81. data/screen_test/elements/screen_overflow_x.rb +9 -0
  82. data/screen_test/elements/screen_overflow_y.frame +1 -0
  83. data/screen_test/elements/screen_overflow_y.rb +9 -0
  84. data/screen_test/elements/text.frame +1 -0
  85. data/screen_test/elements/text.rb +9 -0
  86. data/screen_test/elements/text_multiline.frame +1 -0
  87. data/screen_test/elements/text_multiline.rb +9 -0
  88. data/screen_test/settings/align/box_around.frame +1 -0
  89. data/screen_test/settings/align/box_around.rb +16 -0
  90. data/screen_test/settings/align/box_between.frame +1 -0
  91. data/screen_test/settings/align/box_between.rb +16 -0
  92. data/screen_test/settings/align/box_center.frame +1 -0
  93. data/screen_test/settings/align/box_center.rb +21 -0
  94. data/screen_test/settings/align/box_default.frame +1 -0
  95. data/screen_test/settings/align/box_default.rb +20 -0
  96. data/screen_test/settings/align/box_evenly.frame +1 -0
  97. data/screen_test/settings/align/box_evenly.rb +16 -0
  98. data/screen_test/settings/align/box_left.frame +1 -0
  99. data/screen_test/settings/align/box_left.rb +21 -0
  100. data/screen_test/settings/align/box_right.frame +1 -0
  101. data/screen_test/settings/align/box_right.rb +21 -0
  102. data/screen_test/settings/align/children_center.frame +1 -0
  103. data/screen_test/settings/align/children_center.rb +15 -0
  104. data/screen_test/settings/align/children_left.frame +1 -0
  105. data/screen_test/settings/align/children_left.rb +15 -0
  106. data/screen_test/settings/align/children_right.frame +1 -0
  107. data/screen_test/settings/align/children_right.rb +15 -0
  108. data/screen_test/settings/align/grid_center.frame +1 -0
  109. data/screen_test/settings/align/grid_center.rb +18 -0
  110. data/screen_test/settings/align/grid_default.frame +1 -0
  111. data/screen_test/settings/align/grid_default.rb +17 -0
  112. data/screen_test/settings/align/grid_left.frame +1 -0
  113. data/screen_test/settings/align/grid_left.rb +18 -0
  114. data/screen_test/settings/align/grid_right.frame +1 -0
  115. data/screen_test/settings/align/grid_right.rb +18 -0
  116. data/screen_test/settings/ansi/bold.frame +1 -0
  117. data/screen_test/settings/ansi/bold.rb +14 -0
  118. data/screen_test/settings/ansi/color.frame +1 -0
  119. data/screen_test/settings/ansi/color.rb +37 -0
  120. data/screen_test/settings/ansi/underline.frame +1 -0
  121. data/screen_test/settings/ansi/underline.rb +14 -0
  122. data/screen_test/settings/border.frame +1 -0
  123. data/screen_test/settings/border.rb +13 -0
  124. data/screen_test/settings/flow/box_b2t.frame +1 -0
  125. data/screen_test/settings/flow/box_b2t.rb +26 -0
  126. data/screen_test/settings/flow/box_l2r.frame +1 -0
  127. data/screen_test/settings/flow/box_l2r.rb +26 -0
  128. data/screen_test/settings/flow/box_r2l.frame +1 -0
  129. data/screen_test/settings/flow/box_r2l.rb +26 -0
  130. data/screen_test/settings/flow/box_t2b.frame +1 -0
  131. data/screen_test/settings/flow/box_t2b.rb +26 -0
  132. data/screen_test/settings/flow/grid_b2t.frame +1 -0
  133. data/screen_test/settings/flow/grid_b2t.rb +14 -0
  134. data/screen_test/settings/flow/grid_l2r.frame +1 -0
  135. data/screen_test/settings/flow/grid_l2r.rb +14 -0
  136. data/screen_test/settings/flow/grid_r2l.frame +1 -0
  137. data/screen_test/settings/flow/grid_r2l.rb +14 -0
  138. data/screen_test/settings/flow/grid_t2b.frame +1 -0
  139. data/screen_test/settings/flow/grid_t2b.rb +14 -0
  140. data/screen_test/settings/height/box.frame +1 -0
  141. data/screen_test/settings/height/box.rb +13 -0
  142. data/screen_test/settings/height/box_border_sizing.frame +1 -0
  143. data/screen_test/settings/height/box_border_sizing.rb +15 -0
  144. data/screen_test/settings/height/grid.frame +1 -0
  145. data/screen_test/settings/height/grid.rb +14 -0
  146. data/screen_test/settings/height/overflow_box.frame +1 -0
  147. data/screen_test/settings/height/overflow_box.rb +13 -0
  148. data/screen_test/settings/height/overflow_box_l2r.frame +1 -0
  149. data/screen_test/settings/height/overflow_box_l2r.rb +17 -0
  150. data/screen_test/settings/height/overflow_box_t2b.frame +1 -0
  151. data/screen_test/settings/height/overflow_box_t2b.rb +16 -0
  152. data/screen_test/settings/height/overflow_grid.frame +1 -0
  153. data/screen_test/settings/height/overflow_grid.rb +16 -0
  154. data/screen_test/settings/margin.frame +1 -0
  155. data/screen_test/settings/margin.rb +16 -0
  156. data/screen_test/settings/padding.frame +1 -0
  157. data/screen_test/settings/padding.rb +13 -0
  158. data/screen_test/settings/position/box_left.frame +1 -0
  159. data/screen_test/settings/position/box_left.rb +17 -0
  160. data/screen_test/settings/position/box_left_negative.frame +1 -0
  161. data/screen_test/settings/position/box_left_negative.rb +17 -0
  162. data/screen_test/settings/position/box_top.frame +1 -0
  163. data/screen_test/settings/position/box_top.rb +17 -0
  164. data/screen_test/settings/position/box_top_negative.frame +1 -0
  165. data/screen_test/settings/position/box_top_negative.rb +17 -0
  166. data/screen_test/settings/position/grid_left.frame +1 -0
  167. data/screen_test/settings/position/grid_left.rb +18 -0
  168. data/screen_test/settings/position/grid_left_negative.frame +1 -0
  169. data/screen_test/settings/position/grid_left_negative.rb +18 -0
  170. data/screen_test/settings/position/grid_top.frame +1 -0
  171. data/screen_test/settings/position/grid_top.rb +18 -0
  172. data/screen_test/settings/position/grid_top_negative.frame +1 -0
  173. data/screen_test/settings/position/grid_top_negative.rb +18 -0
  174. data/screen_test/settings/scroll/horiz_box.frame +1 -0
  175. data/screen_test/settings/scroll/horiz_box.rb +17 -0
  176. data/screen_test/settings/scroll/horiz_box_align_center.rb +18 -0
  177. data/screen_test/settings/scroll/horiz_box_align_right.rb +18 -0
  178. data/screen_test/settings/scroll/vert_box.frame +1 -0
  179. data/screen_test/settings/scroll/vert_box.rb +20 -0
  180. data/screen_test/settings/title_font.frame +1 -0
  181. data/screen_test/settings/title_font.rb +12 -0
  182. data/screen_test/settings/valign/box_around.frame +1 -0
  183. data/screen_test/settings/valign/box_around.rb +17 -0
  184. data/screen_test/settings/valign/box_between.frame +1 -0
  185. data/screen_test/settings/valign/box_between.rb +17 -0
  186. data/screen_test/settings/valign/box_bottom.frame +1 -0
  187. data/screen_test/settings/valign/box_bottom.rb +17 -0
  188. data/screen_test/settings/valign/box_default.frame +1 -0
  189. data/screen_test/settings/valign/box_default.rb +16 -0
  190. data/screen_test/settings/valign/box_evenly.frame +1 -0
  191. data/screen_test/settings/valign/box_evenly.rb +17 -0
  192. data/screen_test/settings/valign/box_middle.frame +1 -0
  193. data/screen_test/settings/valign/box_middle.rb +17 -0
  194. data/screen_test/settings/valign/box_top.frame +1 -0
  195. data/screen_test/settings/valign/box_top.rb +17 -0
  196. data/screen_test/settings/valign/grid_bottom.frame +1 -0
  197. data/screen_test/settings/valign/grid_bottom.rb +15 -0
  198. data/screen_test/settings/valign/grid_default.frame +1 -0
  199. data/screen_test/settings/valign/grid_default.rb +14 -0
  200. data/screen_test/settings/valign/grid_middle.frame +1 -0
  201. data/screen_test/settings/valign/grid_middle.rb +15 -0
  202. data/screen_test/settings/valign/grid_top.frame +1 -0
  203. data/screen_test/settings/valign/grid_top.rb +15 -0
  204. data/screen_test/settings/width/box_border_sizing.frame +1 -0
  205. data/screen_test/settings/width/box_border_sizing.rb +15 -0
  206. data/screen_test/settings/width/box_content.frame +1 -0
  207. data/screen_test/settings/width/box_content.rb +15 -0
  208. data/screen_test/settings/width/box_default.frame +1 -0
  209. data/screen_test/settings/width/box_default.rb +14 -0
  210. data/screen_test/settings/width/grid.frame +1 -0
  211. data/screen_test/settings/width/grid.rb +14 -0
  212. data/screen_test/settings/width/overflow_align_center.frame +1 -0
  213. data/screen_test/settings/width/overflow_align_center.rb +14 -0
  214. data/screen_test/settings/width/overflow_align_right.frame +1 -0
  215. data/screen_test/settings/width/overflow_align_right.rb +14 -0
  216. data/screen_test/settings/width/overflow_box.frame +1 -0
  217. data/screen_test/settings/width/overflow_box.rb +13 -0
  218. data/screen_test/settings/width/overflow_box_l2r.frame +1 -0
  219. data/screen_test/settings/width/overflow_box_l2r.rb +16 -0
  220. data/screen_test/settings/width/overflow_box_t2b.frame +1 -0
  221. data/screen_test/settings/width/overflow_box_t2b.rb +17 -0
  222. data/screen_test/settings/width/overflow_grid.frame +1 -0
  223. data/screen_test/settings/width/overflow_grid.rb +14 -0
  224. data/tools/whirled_peas/tools/screen_tester.rb +285 -0
  225. metadata +213 -15
  226. data/bin/title_fonts +0 -6
  227. data/lib/whirled_peas/frame.rb +0 -7
  228. data/lib/whirled_peas/frame/event_loop.rb +0 -91
  229. data/lib/whirled_peas/frame/print_consumer.rb +0 -33
  230. data/lib/whirled_peas/frame/producer.rb +0 -61
  231. data/lib/whirled_peas/template.rb +0 -5
  232. data/lib/whirled_peas/template/element.rb +0 -230
  233. data/lib/whirled_peas/template/settings.rb +0 -530
  234. data/lib/whirled_peas/ui.rb +0 -5
  235. data/lib/whirled_peas/ui/canvas.rb +0 -68
  236. data/lib/whirled_peas/ui/painter.rb +0 -287
  237. data/lib/whirled_peas/ui/screen.rb +0 -63
  238. data/lib/whirled_peas/utils/color.rb +0 -101
@@ -1,232 +1,21 @@
1
- module WhirledPeas
2
- class Command
3
- DEFAULT_REFRESH_RATE = 30
4
-
5
- DEFAULT_LOG_LEVEL = Logger::INFO
6
- DEFAULT_FORMATTER = proc do |severity, datetime, progname, msg|
7
- if msg.is_a?(Exception)
8
- msg = %Q(#{msg.class}: #{msg.to_s}\n #{msg.backtrace.join("\n ")})
9
- end
10
- "[#{severity}] #{datetime.strftime('%Y-%m-%dT%H:%M:%S.%L')} (#{progname}) - #{msg}\n"
11
- end
12
-
13
- def self.command_name
14
- self.name.split('::').last.sub(/Command$/, '').gsub(/([a-z])([A-Z])/, '\1_\2').downcase
15
- end
16
-
17
- def self.build_logger(output, level=DEFAULT_LOG_LEVEL, formatter=DEFAULT_FORMATTER)
18
- logger = Logger.new(output)
19
- logger.level = level
20
- logger.formatter = formatter
21
- logger
22
- end
23
-
24
- attr_reader :args
25
-
26
- def initialize(args)
27
- @args = args
28
- end
29
-
30
- def valid?
31
- @error_text = nil
32
- validate!
33
- @error_text.nil?
34
- end
35
-
36
- def print_error
37
- puts @error_text if @error_text
38
- print_usage
39
- end
40
-
41
- def start
42
- end
43
-
44
- private
45
-
46
- def print_usage
47
- puts "Usage: #{$0} #{self.class.command_name}"
48
- end
49
-
50
- def validate!
51
- # Set @error_text if the options are not valid
52
- end
53
- end
54
-
55
- class TitleFontsCommand < Command
56
- def start
57
- require 'whirled_peas/utils/title_font'
58
-
59
- Utils::TitleFont.fonts.keys.each do |key|
60
- puts Utils::TitleFont.to_s(key.to_s, key)
61
- puts key.inspect
62
- puts
63
- end
64
- end
65
- end
66
-
67
- class ConfigCommand < Command
68
- def start
69
- require config
70
- end
71
-
72
- private
73
-
74
- attr_reader :config
75
-
76
- def validate!
77
- if args.length == 0
78
- @error_text = "#{self.class.command_name} requires a config file"
79
- elsif !File.exist?(args[0])
80
- @error_text = "File not found: #{args[0]}"
81
- elsif args[0][-3..-1] != '.rb'
82
- @error_text = 'Config file should be a .rb file'
83
- else
84
- @config = args[0][0] == '/' ? args[0] : File.join(Dir.pwd, args[0])
85
- end
86
- end
87
-
88
- def print_usage
89
- puts "Usage: #{$0} #{self.class.command_name} <config file>"
90
- end
91
- end
92
-
93
- class StartCommand < ConfigCommand
94
- LOGGER_ID = 'MAIN'
95
-
96
- def start
97
- super
98
- require 'whirled_peas/frame/event_loop'
99
- require 'whirled_peas/frame/producer'
100
-
101
- logger = self.class.build_logger(File.open('whirled_peas.log', 'a'))
102
-
103
- consumer = Frame::EventLoop.new(
104
- WhirledPeas.config.template_factory,
105
- WhirledPeas.config.loading_template_factory,
106
- DEFAULT_REFRESH_RATE,
107
- logger
108
- )
109
- Frame::Producer.produce(consumer, logger) do |producer|
110
- begin
111
- WhirledPeas.config.driver.start(producer)
112
- rescue => e
113
- logger.warn(LOGGER_ID) { 'Driver exited with error, terminating producer...' }
114
- logger.error(LOGGER_ID) { e }
115
- raise
116
- end
117
- end
118
- end
119
- end
120
-
121
- class ListFramesCommand < ConfigCommand
122
- def start
123
- super
124
- require 'whirled_peas/frame/print_consumer'
125
- require 'whirled_peas/frame/producer'
126
-
127
- logger = self.class.build_logger(STDOUT)
128
- Frame::Producer.produce(Frame::PrintConsumer.new, logger) do |producer|
129
- begin
130
- WhirledPeas.config.driver.start(producer)
131
- rescue => e
132
- logger.warn(LOGGER_ID) { 'Driver exited with error, terminating producer...' }
133
- logger.error(LOGGER_ID) { e }
134
- raise
135
- end
136
- end
137
- end
138
- end
139
-
140
- class PlayFrameCommand < ConfigCommand
141
- def start
142
- super
143
-
144
- if args.last == '--debug'
145
- puts WhirledPeas.config.template_factory.build(frame, frame_args).inspect
146
- exit
147
- end
148
-
149
- require 'whirled_peas/frame/event_loop'
150
- require 'whirled_peas/frame/producer'
151
-
152
- logger = self.class.build_logger(File.open('whirled_peas.log', 'a'))
153
-
154
- consumer = Frame::EventLoop.new(
155
- WhirledPeas.config.template_factory,
156
- WhirledPeas.config.loading_template_factory,
157
- DEFAULT_REFRESH_RATE,
158
- logger
159
- )
160
- Frame::Producer.produce(consumer, logger) do |producer|
161
- producer.send_frame(args[1], duration: 5, args: frame_args)
162
- end
163
- end
164
-
165
- private
166
-
167
- attr_reader :frame, :frame_args
168
-
169
- def validate!
170
- super
171
- if !@error_text.nil?
172
- return
173
- elsif args.length < 2
174
- @error_text = "#{self.class.command_name} requires a frame name"
175
- else
176
- require 'json'
177
- @frame = args[1]
178
- @frame_args = {}
179
- JSON.parse(args[2] || '{}').each do |key, value|
180
- @frame_args[key.to_sym] = value
181
- end
182
- end
183
- end
184
-
185
- def print_usage
186
- puts "Usage: #{$0} #{self.class.command_name} <config file> <frame> [args as a JSON string] [--debug]"
187
- end
188
- end
189
-
190
- class LoadingCommand < ConfigCommand
191
- def start
192
- super
193
- unless WhirledPeas.config.loading_template_factory
194
- puts 'No loading screen configured'
195
- exit
196
- end
197
-
198
- if args.last == '--debug'
199
- puts WhirledPeas.config.loading_template_factory.build.inspect
200
- exit
201
- end
202
-
203
- require 'whirled_peas/frame/event_loop'
204
- require 'whirled_peas/frame/producer'
205
-
206
- logger = self.class.build_logger(File.open('whirled_peas.log', 'a'))
207
- consumer = Frame::EventLoop.new(
208
- WhirledPeas.config.template_factory,
209
- WhirledPeas.config.loading_template_factory,
210
- DEFAULT_REFRESH_RATE,
211
- logger
212
- )
213
- Frame::Producer.produce(consumer, logger) { sleep(5) }
214
- end
215
-
216
- private
217
-
218
- def print_usage
219
- puts "Usage: #{$0} #{self.class.command_name} [--debug]"
220
- end
221
- end
1
+ require 'whirled_peas/command/debug'
2
+ require 'whirled_peas/command/fonts'
3
+ require 'whirled_peas/command/frames'
4
+ require 'whirled_peas/command/help'
5
+ require 'whirled_peas/command/play'
6
+ require 'whirled_peas/command/record'
7
+ require 'whirled_peas/command/still'
222
8
 
9
+ module WhirledPeas
223
10
  class CommandLine
224
11
  COMMANDS = [
225
- StartCommand,
226
- ListFramesCommand,
227
- PlayFrameCommand,
228
- LoadingCommand,
229
- TitleFontsCommand
12
+ Command::Debug,
13
+ Command::Fonts,
14
+ Command::Frames,
15
+ Command::Help,
16
+ Command::Play,
17
+ Command::Record,
18
+ Command::Still
230
19
  ].map.with_object({}) { |c, h| h[c.command_name] = c }
231
20
 
232
21
  def initialize(args)
@@ -247,7 +36,7 @@ module WhirledPeas
247
36
  exit(1)
248
37
  end
249
38
 
250
- cmd = COMMANDS[command].new(args)
39
+ cmd = COMMANDS[command].new(args, WhirledPeas.config)
251
40
 
252
41
  unless cmd.valid?
253
42
  cmd.print_error
@@ -264,7 +53,13 @@ module WhirledPeas
264
53
  def print_usage
265
54
  puts "Usage: #{$0} <command> [command options]"
266
55
  puts
267
- puts "Available commands: #{COMMANDS.keys.join(', ')}"
56
+ puts 'Available commands:'
57
+ puts
58
+ max_name_length = 0
59
+ COMMANDS.keys.each { |c| max_name_length = c.length if c.length > max_name_length }
60
+ COMMANDS.each do |name, klass|
61
+ puts " #{name.ljust(max_name_length, ' ')} #{klass.description}"
62
+ end
268
63
  end
269
64
  end
270
65
  end
@@ -1,13 +1,47 @@
1
+ require 'logger'
2
+
1
3
  module WhirledPeas
2
4
  class Config
3
- attr_writer :driver, :template_factory
4
- attr_accessor :loading_template_factory
5
+ # Refreshed rate measured in frames per second
6
+ DEFAULT_REFRESH_RATE = 30
7
+
8
+ DEFAULT_LOG_LEVEL = Logger::INFO
9
+ DEFAULT_LOG_FILE = 'whirled_peas.log'
5
10
 
6
- def driver
7
- unless @driver
8
- raise ConfigurationError, 'driver must be configured'
11
+ # This formatter expects a loggers to send `progname` in each log call. This value
12
+ # should be an all uppercase version of the module or class that is invoking the
13
+ # logger. Ruby's logger supports setting this value on a per-log statement basis
14
+ # when the log message is passed in through a block:
15
+ #
16
+ # logger.<level>(progname, &block)
17
+ #
18
+ # E.g.
19
+ #
20
+ # class Foo
21
+ # def bar
22
+ # logger.warn('FOO') { 'Something fishy happened in #bar' }
23
+ # end
24
+ # end
25
+ #
26
+ # The block format also has the advantage that the evaluation of the block only
27
+ # occurs if the message gets logged. So expensive to calculate debug statements
28
+ # will not impact the performance of the application if the log level is INFO or
29
+ # higher.
30
+ DEFAULT_FORMATTER = proc do |severity, datetime, progname, msg|
31
+ # Convert an instance of an exception into a nicely formatted message string
32
+ if msg.is_a?(Exception)
33
+ msg = %Q(#{msg.class}: #{msg.to_s}\n #{msg.backtrace.join("\n ")})
9
34
  end
10
- @driver
35
+ "[#{severity}] #{datetime.strftime('%Y-%m-%dT%H:%M:%S.%L')} (#{progname}) - #{msg}\n"
36
+ end
37
+
38
+ attr_writer :application, :template_factory, :refresh_rate, :log_level, :log_formatter, :log_file
39
+
40
+ def application
41
+ unless @application
42
+ raise ConfigurationError, 'application must be configured'
43
+ end
44
+ @application
11
45
  end
12
46
 
13
47
  def template_factory
@@ -16,6 +50,22 @@ module WhirledPeas
16
50
  end
17
51
  @template_factory
18
52
  end
53
+
54
+ def refresh_rate
55
+ @refresh_rate || DEFAULT_REFRESH_RATE
56
+ end
57
+
58
+ def log_level
59
+ @log_level || DEFAULT_LOG_LEVEL
60
+ end
61
+
62
+ def log_formatter
63
+ @log_formatter || DEFAULT_FORMATTER
64
+ end
65
+
66
+ def log_file
67
+ @log_file || DEFAULT_LOG_FILE
68
+ end
19
69
  end
20
70
  private_constant :Config
21
71
  end
@@ -0,0 +1,5 @@
1
+ module WhirledPeas
2
+ module Device
3
+ end
4
+ private_constant :Device
5
+ end
@@ -0,0 +1,8 @@
1
+ module WhirledPeas
2
+ module Device
3
+ class NullDevice
4
+ def handle_renders(*)
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,19 @@
1
+ require 'whirled_peas/utils/file_handler'
2
+
3
+ module WhirledPeas
4
+ module Device
5
+ class OutputFile
6
+ def initialize(file)
7
+ @file = file
8
+ end
9
+
10
+ def handle_renders(renders)
11
+ Utils::FileHandler.write(file, renders)
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :file
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ require 'highline'
2
+
3
+ module WhirledPeas
4
+ module Device
5
+ class Screen
6
+ def initialize(refresh_rate, output: STDOUT)
7
+ @refresh_rate = refresh_rate
8
+ @output = output
9
+ end
10
+
11
+ def handle_renders(renders)
12
+ renders.each do |strokes|
13
+ frame_at = Time.now
14
+ output.print(strokes)
15
+ output.flush
16
+ next_frame_at = frame_at + 1.0 / refresh_rate
17
+ sleep([0, next_frame_at - Time.now].max)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :refresh_rate, :output
24
+ end
25
+ end
26
+ end
@@ -2,4 +2,6 @@ module WhirledPeas
2
2
  class Error < StandardError; end
3
3
 
4
4
  class ConfigurationError < Error; end
5
+
6
+ class SettingsError < Error; end
5
7
  end
@@ -0,0 +1,19 @@
1
+ module WhirledPeas
2
+ module Graphics
3
+ class << self
4
+ attr_accessor :debug
5
+
6
+ def debugger(string_or_proc)
7
+ return unless @debug
8
+ @debugger ||= Logger.new(STDOUT, level: Logger::DEBUG)
9
+ if string_or_proc.is_a?(Proc)
10
+ string = string_or_proc.call
11
+ else
12
+ string = string_or_proc
13
+ end
14
+ @debugger.debug(string)
15
+ end
16
+ end
17
+ end
18
+ private_constant :Graphics
19
+ end
@@ -0,0 +1,101 @@
1
+ require_relative 'container_painter'
2
+ require_relative 'container_dimensions'
3
+
4
+ module WhirledPeas
5
+ module Graphics
6
+ class BoxPainter < ContainerPainter
7
+ def paint(canvas, left, top, &block)
8
+ super
9
+ canvas_coords = coords(left, top)
10
+ content_canvas = canvas.child(
11
+ canvas_coords.content_left,
12
+ canvas_coords.content_top,
13
+ dimensions.content_width,
14
+ dimensions.content_height
15
+ )
16
+ return unless content_canvas.writable?
17
+ if settings.horizontal_flow?
18
+ paint_horizontally(content_canvas, canvas_coords, &block)
19
+ else
20
+ paint_vertically(content_canvas, canvas_coords, &block)
21
+ end
22
+ end
23
+
24
+ def dimensions
25
+ @dimensions ||= begin
26
+ content_width = 0
27
+ content_height = 0
28
+ if settings.horizontal_flow?
29
+ each_child do |child|
30
+ content_width += child.dimensions.outer_width
31
+ if child.dimensions.outer_height > content_height
32
+ content_height = child.dimensions.outer_height
33
+ end
34
+ end
35
+ else
36
+ each_child do |child|
37
+ if child.dimensions.outer_width > content_width
38
+ content_width = child.dimensions.outer_width
39
+ end
40
+ content_height += child.dimensions.outer_height
41
+ end
42
+ end
43
+ ContainerDimensions.new(settings, content_width, content_height)
44
+ end
45
+ end
46
+
47
+ def each_child(&block)
48
+ if settings.reverse_flow?
49
+ children.reverse.each(&block)
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def paint_horizontally(canvas, canvas_coords, &block)
58
+ stroke_left = canvas_coords.content_left
59
+ stroke_top = canvas_coords.content_top
60
+ children_width = 0
61
+ each_child { |c| children_width += c.dimensions.outer_width }
62
+ left_offset, spacing_offset = horiz_justify_offset(children_width)
63
+ stroke_left += left_offset
64
+ each_child do |child|
65
+ top_offset, _ = vert_justify_offset(child.dimensions.outer_height)
66
+ child_width = child.dimensions.outer_width
67
+ child_canvas = canvas.child(
68
+ stroke_left,
69
+ stroke_top + top_offset,
70
+ child_width,
71
+ child.dimensions.outer_height
72
+ )
73
+ child.paint(child_canvas, stroke_left, stroke_top + top_offset, &block)
74
+ stroke_left += child_width + spacing_offset
75
+ end
76
+ end
77
+
78
+ def paint_vertically(canvas, canvas_coords, &block)
79
+ stroke_left = canvas_coords.content_left
80
+ stroke_top = canvas_coords.content_top
81
+ children_height = 0
82
+ each_child { |c| children_height += c.dimensions.outer_height }
83
+ top_offset, spacing_offset = vert_justify_offset(children_height)
84
+ stroke_top += top_offset
85
+ each_child do |child|
86
+ left_offset, _ = horiz_justify_offset(child.dimensions.outer_width)
87
+ child_height = child.dimensions.outer_height
88
+ child_canvas = canvas.child(
89
+ stroke_left + left_offset,
90
+ stroke_top ,
91
+ child.dimensions.outer_width,
92
+ child_height
93
+ )
94
+ child.paint(child_canvas, stroke_left + left_offset, stroke_top, &block)
95
+ stroke_top += child_height + spacing_offset
96
+ end
97
+ end
98
+ end
99
+ private_constant :BoxPainter
100
+ end
101
+ end