whirled_peas 0.5.0 → 0.9.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 (291) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +1 -0
  4. data/CHANGELOG.md +31 -0
  5. data/README.md +748 -137
  6. data/Rakefile +4 -32
  7. data/bin/easing +33 -0
  8. data/bin/reset_cursor +11 -0
  9. data/bin/screen_test +68 -0
  10. data/examples/graph.rb +54 -0
  11. data/examples/intro.rb +52 -0
  12. data/examples/scrolling.rb +54 -0
  13. data/lib/whirled_peas.rb +4 -12
  14. data/lib/whirled_peas/animator.rb +5 -0
  15. data/lib/whirled_peas/animator/debug_consumer.rb +17 -0
  16. data/lib/whirled_peas/animator/easing.rb +72 -0
  17. data/lib/whirled_peas/animator/frame.rb +5 -0
  18. data/lib/whirled_peas/animator/frameset.rb +33 -0
  19. data/lib/whirled_peas/animator/producer.rb +35 -0
  20. data/lib/whirled_peas/animator/renderer_consumer.rb +31 -0
  21. data/lib/whirled_peas/command.rb +5 -0
  22. data/lib/whirled_peas/command/base.rb +86 -0
  23. data/lib/whirled_peas/command/config_command.rb +44 -0
  24. data/lib/whirled_peas/command/debug.rb +21 -0
  25. data/lib/whirled_peas/command/fonts.rb +22 -0
  26. data/lib/whirled_peas/command/frame_command.rb +34 -0
  27. data/lib/whirled_peas/command/frames.rb +24 -0
  28. data/lib/whirled_peas/command/help.rb +38 -0
  29. data/lib/whirled_peas/command/play.rb +108 -0
  30. data/lib/whirled_peas/command/record.rb +57 -0
  31. data/lib/whirled_peas/command/still.rb +29 -0
  32. data/lib/whirled_peas/command_line.rb +22 -220
  33. data/lib/whirled_peas/config.rb +56 -6
  34. data/lib/whirled_peas/device.rb +5 -0
  35. data/lib/whirled_peas/device/null_device.rb +8 -0
  36. data/lib/whirled_peas/device/output_file.rb +19 -0
  37. data/lib/whirled_peas/device/screen.rb +26 -0
  38. data/lib/whirled_peas/graphics.rb +14 -0
  39. data/lib/whirled_peas/graphics/box_painter.rb +48 -47
  40. data/lib/whirled_peas/graphics/canvas.rb +22 -11
  41. data/lib/whirled_peas/graphics/composer.rb +98 -0
  42. data/lib/whirled_peas/graphics/container_coords.rb +24 -13
  43. data/lib/whirled_peas/graphics/container_dimensions.rb +37 -9
  44. data/lib/whirled_peas/graphics/container_painter.rb +283 -36
  45. data/lib/whirled_peas/graphics/content_dimensions.rb +19 -0
  46. data/lib/whirled_peas/graphics/content_painter.rb +20 -0
  47. data/lib/whirled_peas/graphics/debugger.rb +10 -1
  48. data/lib/whirled_peas/graphics/graph_dimensions.rb +12 -0
  49. data/lib/whirled_peas/graphics/graph_painter.rb +95 -0
  50. data/lib/whirled_peas/graphics/grid_painter.rb +25 -15
  51. data/lib/whirled_peas/graphics/mock_screen.rb +1 -1
  52. data/lib/whirled_peas/graphics/painter.rb +15 -6
  53. data/lib/whirled_peas/graphics/renderer.rb +13 -30
  54. data/lib/whirled_peas/graphics/text_painter.rb +11 -13
  55. data/lib/whirled_peas/settings/alignment.rb +24 -0
  56. data/lib/whirled_peas/settings/bg_color.rb +2 -0
  57. data/lib/whirled_peas/settings/border.rb +16 -16
  58. data/lib/whirled_peas/settings/color.rb +13 -14
  59. data/lib/whirled_peas/settings/container_settings.rb +133 -40
  60. data/lib/whirled_peas/settings/debugger.rb +9 -0
  61. data/lib/whirled_peas/settings/display_flow.rb +13 -11
  62. data/lib/whirled_peas/settings/element_settings.rb +8 -3
  63. data/lib/whirled_peas/settings/graph_settings.rb +21 -0
  64. data/lib/whirled_peas/settings/grid_settings.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 +34 -0
  68. data/lib/whirled_peas/settings/text_color.rb +2 -0
  69. data/lib/whirled_peas/settings/vert_alignment.rb +24 -0
  70. data/lib/whirled_peas/utils/ansi.rb +13 -0
  71. data/lib/whirled_peas/utils/file_handler.rb +57 -0
  72. data/lib/whirled_peas/utils/title_font.rb +1 -1
  73. data/lib/whirled_peas/version.rb +1 -1
  74. data/screen_test/{rendered/elements → elements}/box.frame +1 -1
  75. data/screen_test/{rendered/elements → elements}/box.rb +0 -0
  76. data/screen_test/elements/graph.frame +1 -0
  77. data/screen_test/elements/graph.rb +12 -0
  78. data/screen_test/elements/grid.frame +1 -0
  79. data/screen_test/{rendered/elements → elements}/grid.rb +0 -0
  80. data/screen_test/elements/screen_overflow_x.frame +1 -0
  81. data/screen_test/{rendered/elements/screen_overflow.rb → elements/screen_overflow_x.rb} +0 -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/{rendered/elements → elements}/text.rb +0 -0
  86. data/screen_test/elements/text_multiline.frame +1 -0
  87. data/screen_test/{rendered/elements → elements}/text_multiline.rb +0 -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/{rendered/settings/align/box.rb → settings/align/box_center.rb} +5 -4
  94. data/screen_test/settings/align/box_default.frame +1 -0
  95. data/screen_test/{rendered/settings/align/grid.rb → settings/align/box_default.rb} +6 -6
  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/{rendered/settings/width/overflow_box_t2b.rb → settings/ansi/bold.rb} +4 -5
  118. data/screen_test/{rendered/settings → settings}/ansi/color.frame +1 -1
  119. data/screen_test/{rendered/settings → settings}/ansi/color.rb +0 -0
  120. data/screen_test/settings/ansi/underline.frame +1 -0
  121. data/screen_test/{rendered/settings → settings}/ansi/underline.rb +4 -5
  122. data/screen_test/{rendered/settings → settings}/border.frame +1 -1
  123. data/screen_test/{rendered/settings → settings}/border.rb +1 -1
  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/{rendered/settings/margin.rb → settings/flow/grid_l2r.rb} +4 -4
  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/{rendered/settings → settings}/height/box.rb +0 -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/{rendered/settings → settings}/height/grid.rb +0 -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/{rendered/settings → settings}/height/overflow_grid.rb +0 -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/{rendered/settings → settings}/position/box_left.rb +2 -2
  160. data/screen_test/settings/position/box_left_negative.frame +1 -0
  161. data/screen_test/{rendered/settings → settings}/position/box_left_negative.rb +2 -2
  162. data/screen_test/settings/position/box_top.frame +1 -0
  163. data/screen_test/{rendered/settings → settings}/position/box_top.rb +1 -1
  164. data/screen_test/settings/position/box_top_negative.frame +1 -0
  165. data/screen_test/{rendered/settings → settings}/position/box_top_negative.rb +2 -2
  166. data/screen_test/settings/position/grid_left.frame +1 -0
  167. data/screen_test/{rendered/settings → settings}/position/grid_left.rb +1 -1
  168. data/screen_test/settings/position/grid_left_negative.frame +1 -0
  169. data/screen_test/{rendered/settings → settings}/position/grid_left_negative.rb +1 -1
  170. data/screen_test/settings/position/grid_top.frame +1 -0
  171. data/screen_test/{rendered/settings → settings}/position/grid_top.rb +1 -1
  172. data/screen_test/settings/position/grid_top_negative.frame +1 -0
  173. data/screen_test/{rendered/settings → settings}/position/grid_top_negative.rb +1 -1
  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/{rendered/settings → settings}/title_font.rb +1 -1
  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/{rendered/settings/width/box.rb → settings/width/box_default.rb} +3 -2
  210. data/screen_test/settings/width/grid.frame +1 -0
  211. data/screen_test/{rendered/settings → settings}/width/grid.rb +2 -2
  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/{rendered/settings → settings}/width/overflow_grid.rb +0 -0
  224. data/tools/whirled_peas/tools/screen_tester.rb +285 -0
  225. metadata +192 -98
  226. data/bin/debug +0 -35
  227. data/lib/whirled_peas/debugger.rb +0 -80
  228. data/lib/whirled_peas/frame.rb +0 -6
  229. data/lib/whirled_peas/frame/consumer.rb +0 -30
  230. data/lib/whirled_peas/frame/debug_consumer.rb +0 -30
  231. data/lib/whirled_peas/frame/event_loop.rb +0 -90
  232. data/lib/whirled_peas/frame/producer.rb +0 -67
  233. data/lib/whirled_peas/graphics/screen.rb +0 -65
  234. data/lib/whirled_peas/graphics/text_dimensions.rb +0 -15
  235. data/lib/whirled_peas/settings/text_align.rb +0 -19
  236. data/lib/whirled_peas/template.rb +0 -5
  237. data/lib/whirled_peas/template/box_element.rb +0 -8
  238. data/lib/whirled_peas/template/composer.rb +0 -68
  239. data/lib/whirled_peas/template/container.rb +0 -28
  240. data/lib/whirled_peas/template/debugger.rb +0 -34
  241. data/lib/whirled_peas/template/element.rb +0 -13
  242. data/lib/whirled_peas/template/grid_element.rb +0 -8
  243. data/lib/whirled_peas/template/text_element.rb +0 -24
  244. data/screen_test/rendered/elements/grid.frame +0 -1
  245. data/screen_test/rendered/elements/text.frame +0 -1
  246. data/screen_test/rendered/elements/text_multiline.frame +0 -1
  247. data/screen_test/rendered/settings/align/box.frame +0 -1
  248. data/screen_test/rendered/settings/align/children_center.frame +0 -1
  249. data/screen_test/rendered/settings/align/children_center.rb +0 -13
  250. data/screen_test/rendered/settings/align/children_left.frame +0 -1
  251. data/screen_test/rendered/settings/align/children_left.rb +0 -13
  252. data/screen_test/rendered/settings/align/children_right.frame +0 -1
  253. data/screen_test/rendered/settings/align/children_right.rb +0 -13
  254. data/screen_test/rendered/settings/align/grid.frame +0 -1
  255. data/screen_test/rendered/settings/ansi/bold.frame +0 -1
  256. data/screen_test/rendered/settings/ansi/bold.rb +0 -15
  257. data/screen_test/rendered/settings/ansi/underline.frame +0 -1
  258. data/screen_test/rendered/settings/flow/l2r.frame +0 -1
  259. data/screen_test/rendered/settings/flow/l2r.rb +0 -24
  260. data/screen_test/rendered/settings/flow/t2b.frame +0 -1
  261. data/screen_test/rendered/settings/flow/t2b.rb +0 -24
  262. data/screen_test/rendered/settings/height/box.frame +0 -1
  263. data/screen_test/rendered/settings/height/grid.frame +0 -1
  264. data/screen_test/rendered/settings/height/overflow_box.frame +0 -1
  265. data/screen_test/rendered/settings/height/overflow_box.rb +0 -13
  266. data/screen_test/rendered/settings/height/overflow_box_l2r.frame +0 -1
  267. data/screen_test/rendered/settings/height/overflow_box_l2r.rb +0 -15
  268. data/screen_test/rendered/settings/height/overflow_box_t2b.frame +0 -1
  269. data/screen_test/rendered/settings/height/overflow_box_t2b.rb +0 -14
  270. data/screen_test/rendered/settings/height/overflow_grid.frame +0 -1
  271. data/screen_test/rendered/settings/margin.frame +0 -1
  272. data/screen_test/rendered/settings/padding.frame +0 -1
  273. data/screen_test/rendered/settings/padding.rb +0 -11
  274. data/screen_test/rendered/settings/position/box_left.frame +0 -1
  275. data/screen_test/rendered/settings/position/box_left_negative.frame +0 -1
  276. data/screen_test/rendered/settings/position/box_top.frame +0 -1
  277. data/screen_test/rendered/settings/position/box_top_negative.frame +0 -1
  278. data/screen_test/rendered/settings/position/grid_left.frame +0 -1
  279. data/screen_test/rendered/settings/position/grid_left_negative.frame +0 -1
  280. data/screen_test/rendered/settings/position/grid_top.frame +0 -1
  281. data/screen_test/rendered/settings/position/grid_top_negative.frame +0 -1
  282. data/screen_test/rendered/settings/title_font.frame +0 -1
  283. data/screen_test/rendered/settings/width/box.frame +0 -1
  284. data/screen_test/rendered/settings/width/grid.frame +0 -1
  285. data/screen_test/rendered/settings/width/overflow_box.frame +0 -1
  286. data/screen_test/rendered/settings/width/overflow_box.rb +0 -11
  287. data/screen_test/rendered/settings/width/overflow_box_l2r.frame +0 -1
  288. data/screen_test/rendered/settings/width/overflow_box_l2r.rb +0 -14
  289. data/screen_test/rendered/settings/width/overflow_box_t2b.frame +0 -1
  290. data/screen_test/rendered/settings/width/overflow_grid.frame +0 -1
  291. data/screen_test/screen_tester.rb +0 -191
@@ -0,0 +1,5 @@
1
+ module WhirledPeas
2
+ module Animator
3
+ Frame = Struct.new(:name, :args)
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 'easing'
2
+ require_relative 'frame'
3
+
4
+ module WhirledPeas
5
+ module Animator
6
+ class Frameset
7
+ def initialize(frame_slots, easing, effect)
8
+ @frame_slots = frame_slots
9
+ @easing = Easing.new(easing, effect)
10
+ @frames = []
11
+ end
12
+
13
+ def add_frame(name, args: {})
14
+ frames << [name, args]
15
+ end
16
+
17
+ # Yield each frame in an "eased" order
18
+ def each_frame(&block)
19
+ frame_slots.times do |i|
20
+ input = i.to_f / (frame_slots - 1)
21
+ eased_value = @easing.ease(input)
22
+ index = (eased_value * (frames.length - 1)).floor
23
+ yield *frames[index]
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :frame_slots, :easing, :frames
30
+ end
31
+ private_constant :Frameset
32
+ end
33
+ end
@@ -0,0 +1,35 @@
1
+ require_relative 'easing'
2
+ require_relative 'frameset'
3
+
4
+ module WhirledPeas
5
+ module Animator
6
+ class Producer
7
+ def self.produce(consumer, refresh_rate)
8
+ producer = new(consumer, refresh_rate)
9
+ yield producer
10
+ consumer.process
11
+ end
12
+
13
+ def initialize(consumer, refresh_rate)
14
+ @consumer = consumer
15
+ @refresh_rate = refresh_rate
16
+ end
17
+
18
+ def add_frame(name, duration: nil, args: {})
19
+ frameset(duration || 1 / refresh_rate) do |fs|
20
+ fs.add_frame(name, args: args)
21
+ end
22
+ end
23
+
24
+ def frameset(duration, easing: :linear, effect: :in_out, &block)
25
+ fs = Frameset.new((duration * refresh_rate).round, easing, effect)
26
+ yield fs
27
+ consumer.add_frameset(fs)
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :consumer, :refresh_rate
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ require 'whirled_peas/graphics/renderer'
2
+ require 'whirled_peas/utils/ansi'
3
+
4
+ module WhirledPeas
5
+ module Animator
6
+ class RendererConsumer
7
+ def initialize(template_factory, device, width, height)
8
+ @template_factory = template_factory
9
+ @device = device
10
+ @width = width
11
+ @height = height
12
+ @renders = []
13
+ end
14
+
15
+ def add_frameset(frameset)
16
+ frameset.each_frame do |frame, args|
17
+ template = template_factory.build(frame, args)
18
+ renders << Graphics::Renderer.new(template, width, height).paint
19
+ end
20
+ end
21
+
22
+ def process
23
+ device.handle_renders(renders)
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :template_factory, :device, :width, :height, :renders
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ module WhirledPeas
2
+ module Command
3
+ end
4
+ private_constant :Command
5
+ end
@@ -0,0 +1,86 @@
1
+ module WhirledPeas
2
+ module Command
3
+ # Abstract base class for all commands
4
+ class Base
5
+ # Returns the name of the command as expected by the command line script. By convention,
6
+ # this name is snake case version of the class name, e.g. the command `do_something`
7
+ # would be implemented by `WhirledPeas::Command::DoSomething`, which needs to inherit
8
+ # from this class.
9
+ def self.command_name
10
+ name.split('::').last.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
11
+ end
12
+
13
+ def self.description
14
+ end
15
+
16
+ def self.print_usage
17
+ puts description
18
+ end
19
+
20
+ attr_reader :args
21
+
22
+ def initialize(args, config)
23
+ @args = args
24
+ @config = config
25
+ end
26
+
27
+ # Returns the name of the command as expected by the command line script. By convention,
28
+ # this name is snake case version of the class name, e.g. the command `do_something`
29
+ # would be implemented by `WhirledPeas::Command::DoSomething`, which needs to inherit
30
+ # from this class.
31
+ def command_name
32
+ self.class.command_name
33
+ end
34
+
35
+ def build_logger
36
+ require 'logger'
37
+
38
+ if config.log_file.is_a?(IO)
39
+ output = config.log_file
40
+ else
41
+ File.open(config.log_file, 'a')
42
+ end
43
+
44
+ logger = Logger.new(output)
45
+ logger.level = config.log_level
46
+ logger.formatter = config.log_formatter
47
+ logger
48
+ end
49
+
50
+ # @return [true|false] true if all of the required options were provided
51
+ def valid?
52
+ @error_text = nil
53
+ validate!
54
+ @error_text.nil?
55
+ end
56
+
57
+ # Display the validation error and print a usage statement
58
+ def print_error
59
+ puts @error_text if @error_text
60
+ print_usage
61
+ end
62
+
63
+ # Commands that inherit from this class must override this method
64
+ def start
65
+ end
66
+
67
+ private
68
+
69
+ attr_reader :config
70
+
71
+ def print_usage
72
+ puts ["Usage: #{$0} #{command_name}", *options_usage].join(' ')
73
+ end
74
+
75
+ # Commands that inherit from this class can override this method to validate
76
+ # command line options
77
+ def validate!
78
+ # Set @error_text if the options are not valid
79
+ end
80
+
81
+ def options_usage
82
+ end
83
+ end
84
+ private_constant :Base
85
+ end
86
+ end
@@ -0,0 +1,44 @@
1
+ require_relative 'base'
2
+
3
+ module WhirledPeas
4
+ module Command
5
+ # Abstract command that expects a config file as an argument and then requires the
6
+ # specified file. All implementing classes must call `super` if they override `start`
7
+ # or `validate!`
8
+ class ConfigCommand < Base
9
+ def start
10
+ require config_file
11
+ rescue => e
12
+ puts "Error loading #{config_file}"
13
+ puts e.message
14
+ exit(1)
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :config_file
20
+
21
+ def validate!
22
+ super
23
+ # Note that the main script consumes the <command> argument from ARGV, so we
24
+ # expect the config file to be at index 0.
25
+ config_file = args.shift
26
+ if config_file.nil?
27
+ @error_text = "#{command_name} requires a config file"
28
+ elsif !File.exist?(config_file)
29
+ @error_text = "File not found: #{config_file}"
30
+ elsif config_file[-3..-1] != '.rb'
31
+ @error_text = 'Config file should be a .rb file'
32
+ else
33
+ # We think we have a valid ruby config file, set the absolute path to @config
34
+ @config_file = config_file[0] == '/' ? config_file : File.join(Dir.pwd, config_file)
35
+ end
36
+ end
37
+
38
+ def options_usage
39
+ '<config file>'
40
+ end
41
+ end
42
+ private_constant :ConfigCommand
43
+ end
44
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'frame_command'
2
+
3
+ module WhirledPeas
4
+ module Command
5
+ # Display the template tree for a single frame with the specified arguments.
6
+ class Debug < FrameCommand
7
+ def self.description
8
+ 'Print template tree for specified frame'
9
+ end
10
+
11
+ def start
12
+ super
13
+
14
+ require 'whirled_peas/graphics/debugger'
15
+
16
+ template = config.template_factory.build(frame, frame_args)
17
+ puts Graphics::Debugger.new(template).debug
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'base'
2
+
3
+ module WhirledPeas
4
+ module Command
5
+ # List title fonts installed on the user's system and print sample text in each.
6
+ class Fonts < Base
7
+ def self.description
8
+ 'List installed title fonts with sample text'
9
+ end
10
+
11
+ def start
12
+ require 'whirled_peas/utils/title_font'
13
+
14
+ Utils::TitleFont.fonts.keys.each do |key|
15
+ puts Utils::TitleFont.to_s(key.to_s, key)
16
+ puts key.inspect
17
+ puts
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ require_relative 'config_command'
2
+
3
+ module WhirledPeas
4
+ module Command
5
+ class FrameCommand < ConfigCommand
6
+ private
7
+
8
+ attr_reader :frame, :frame_args
9
+
10
+ def validate!
11
+ super
12
+ frame = args.shift
13
+ raw_args = args.shift
14
+ if frame.nil?
15
+ @error_text = "#{command_name} requires a frame name"
16
+ else
17
+ @frame = frame
18
+ @frame_args = {}
19
+ return if raw_args.nil?
20
+
21
+ require 'json'
22
+
23
+ JSON.parse(raw_args || '{}').each do |key, value|
24
+ @frame_args[key.to_sym] = value
25
+ end
26
+ end
27
+ end
28
+
29
+ def options_usage
30
+ "#{super} <frame> [args as a JSON string]"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'config_command'
2
+
3
+ module WhirledPeas
4
+ module Command
5
+ class Frames < ConfigCommand
6
+ def self.description
7
+ 'Print out list of frames generated by application'
8
+ end
9
+
10
+ def start
11
+ super
12
+
13
+ require 'whirled_peas/animator/debug_consumer'
14
+ require 'whirled_peas/animator/producer'
15
+
16
+ Animator::Producer.produce(
17
+ Animator::DebugConsumer.new, WhirledPeas.config.refresh_rate
18
+ ) do |producer|
19
+ config.application.start(producer)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ require_relative 'base'
2
+
3
+ module WhirledPeas
4
+ module Command
5
+ class Help < Base
6
+ def self.description
7
+ 'Show detailed help for a command'
8
+ end
9
+
10
+ def start
11
+ class_name = cmd.split('_').map(&:capitalize).join
12
+ klass = Command.const_get(class_name)
13
+ klass.print_usage
14
+ rescue NameError
15
+ puts "Unrecognized command: #{cmd}"
16
+ exit(1)
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :cmd
22
+
23
+ def validate!
24
+ super
25
+ cmd = args.shift
26
+ if cmd.nil?
27
+ @error_text = "#{command_name} requires a command"
28
+ else
29
+ @cmd = cmd
30
+ end
31
+ end
32
+
33
+ def options_usage
34
+ [*super, '<command>'].join(' ')
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,108 @@
1
+
2
+ require_relative 'base'
3
+
4
+ module WhirledPeas
5
+ module Command
6
+ # Start the animation
7
+ class Play < Base
8
+ class NullPlayer
9
+ def play
10
+ end
11
+ end
12
+
13
+ class ApplicationPlayer
14
+ def initialize(app_config_file, config, logger)
15
+ @app_config_file = app_config_file
16
+ @config = config
17
+ @logger = logger
18
+ end
19
+
20
+ def play
21
+ require app_config_file
22
+
23
+ require 'whirled_peas/animator/renderer_consumer'
24
+ require 'whirled_peas/animator/producer'
25
+ require 'whirled_peas/device/screen'
26
+ require 'whirled_peas/utils/ansi'
27
+
28
+ Utils::Ansi.with_screen do |width, height|
29
+ consumer = Animator::RendererConsumer.new(
30
+ WhirledPeas.config.template_factory,
31
+ Device::Screen.new(WhirledPeas.config.refresh_rate),
32
+ width,
33
+ height
34
+ )
35
+ Animator::Producer.produce(
36
+ consumer, WhirledPeas.config.refresh_rate
37
+ ) do |producer|
38
+ config.application.start(producer)
39
+ end
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ attr_reader :app_config_file, :config, :logger
46
+ end
47
+
48
+ class FilePlayer
49
+ def initialize(wpz_file)
50
+ @wpz_file = wpz_file
51
+ end
52
+
53
+ def play
54
+ require 'whirled_peas/device/screen'
55
+ require 'whirled_peas/utils/ansi'
56
+ require 'whirled_peas/utils/file_handler'
57
+
58
+ Utils::Ansi.with_screen do
59
+ screen = Device::Screen.new(WhirledPeas.config.refresh_rate)
60
+ renders = Utils::FileHandler.read(wpz_file)
61
+ screen.handle_renders(renders)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ attr_reader :wpz_file
68
+ end
69
+
70
+ def self.description
71
+ 'Play an animation from an application or prerecorded file'
72
+ end
73
+
74
+ def start
75
+ super
76
+ player.play
77
+ end
78
+
79
+ private
80
+
81
+ attr_reader :player
82
+
83
+ def validate!
84
+ super
85
+ @player = NullPlayer
86
+ file = args.shift
87
+ if file.nil?
88
+ @error_text = "#{command_name} requires an config file or frames file file"
89
+ elsif !File.exist?(file)
90
+ @error_text = "File not found: #{file}"
91
+ else
92
+ full_path_file = file[0] == '/' ? file : File.join(Dir.pwd, file)
93
+ if full_path_file.end_with?('.wpz')
94
+ @player = FilePlayer.new(full_path_file)
95
+ elsif full_path_file.end_with?('.rb')
96
+ @player = ApplicationPlayer.new(full_path_file, config, build_logger)
97
+ else
98
+ @error_text = "Unsupported file type: .#{file.split('.').last}, epxecting .rb or .wpz"
99
+ end
100
+ end
101
+ end
102
+
103
+ def options_usage
104
+ '<config/wpz file>'
105
+ end
106
+ end
107
+ end
108
+ end