whirled_peas 0.7.0 → 0.10.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 (220) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +25 -0
  4. data/README.md +467 -120
  5. data/Rakefile +1 -20
  6. data/bin/easing +33 -0
  7. data/bin/reset_cursor +11 -0
  8. data/bin/screen_test +68 -0
  9. data/examples/graph.rb +54 -0
  10. data/examples/intro.rb +3 -3
  11. data/examples/scrolling.rb +5 -4
  12. data/lib/data/themes.yaml +13 -0
  13. data/lib/whirled_peas.rb +12 -6
  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 +43 -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 +111 -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/themes.rb +76 -0
  33. data/lib/whirled_peas/command_line.rb +24 -212
  34. data/lib/whirled_peas/config.rb +56 -6
  35. data/lib/whirled_peas/device.rb +5 -0
  36. data/lib/whirled_peas/device/null_device.rb +8 -0
  37. data/lib/whirled_peas/device/output_file.rb +19 -0
  38. data/lib/whirled_peas/device/screen.rb +26 -0
  39. data/lib/whirled_peas/graphics/composer.rb +23 -2
  40. data/lib/whirled_peas/graphics/container_painter.rb +91 -0
  41. data/lib/whirled_peas/graphics/content_dimensions.rb +19 -0
  42. data/lib/whirled_peas/graphics/content_painter.rb +20 -0
  43. data/lib/whirled_peas/graphics/graph_dimensions.rb +12 -0
  44. data/lib/whirled_peas/graphics/graph_painter.rb +97 -0
  45. data/lib/whirled_peas/graphics/grid_painter.rb +1 -4
  46. data/lib/whirled_peas/graphics/painter.rb +10 -0
  47. data/lib/whirled_peas/graphics/renderer.rb +8 -2
  48. data/lib/whirled_peas/graphics/text_painter.rb +7 -11
  49. data/lib/whirled_peas/settings/border.rb +25 -4
  50. data/lib/whirled_peas/settings/container_settings.rb +2 -4
  51. data/lib/whirled_peas/settings/element_settings.rb +12 -3
  52. data/lib/whirled_peas/settings/graph_settings.rb +21 -0
  53. data/lib/whirled_peas/settings/grid_settings.rb +7 -0
  54. data/lib/whirled_peas/settings/text_settings.rb +1 -0
  55. data/lib/whirled_peas/settings/theme.rb +40 -0
  56. data/lib/whirled_peas/settings/theme_library.rb +63 -0
  57. data/lib/whirled_peas/utils/ansi.rb +13 -0
  58. data/lib/whirled_peas/utils/file_handler.rb +57 -0
  59. data/lib/whirled_peas/version.rb +1 -1
  60. data/screen_test/elements/box.frame +1 -1
  61. data/screen_test/elements/box.rb +1 -1
  62. data/screen_test/elements/graph.frame +1 -0
  63. data/screen_test/elements/graph.rb +12 -0
  64. data/screen_test/elements/grid.frame +1 -1
  65. data/screen_test/elements/grid.rb +1 -1
  66. data/screen_test/elements/screen_overflow_x.frame +1 -1
  67. data/screen_test/elements/screen_overflow_x.rb +1 -1
  68. data/screen_test/elements/screen_overflow_y.frame +1 -1
  69. data/screen_test/elements/screen_overflow_y.rb +1 -1
  70. data/screen_test/elements/text.frame +1 -1
  71. data/screen_test/elements/text.rb +1 -1
  72. data/screen_test/elements/text_multiline.frame +1 -1
  73. data/screen_test/elements/text_multiline.rb +1 -1
  74. data/screen_test/elements/theme.frame +1 -0
  75. data/screen_test/elements/theme.rb +26 -0
  76. data/screen_test/settings/align/box_around.frame +1 -1
  77. data/screen_test/settings/align/box_around.rb +1 -1
  78. data/screen_test/settings/align/box_between.frame +1 -1
  79. data/screen_test/settings/align/box_between.rb +1 -1
  80. data/screen_test/settings/align/box_center.frame +1 -1
  81. data/screen_test/settings/align/box_center.rb +1 -1
  82. data/screen_test/settings/align/box_default.frame +1 -1
  83. data/screen_test/settings/align/box_default.rb +1 -1
  84. data/screen_test/settings/align/box_evenly.frame +1 -1
  85. data/screen_test/settings/align/box_evenly.rb +1 -1
  86. data/screen_test/settings/align/box_left.frame +1 -1
  87. data/screen_test/settings/align/box_left.rb +1 -1
  88. data/screen_test/settings/align/box_right.frame +1 -1
  89. data/screen_test/settings/align/box_right.rb +1 -1
  90. data/screen_test/settings/align/children_center.frame +1 -1
  91. data/screen_test/settings/align/children_center.rb +1 -1
  92. data/screen_test/settings/align/children_left.frame +1 -1
  93. data/screen_test/settings/align/children_left.rb +1 -1
  94. data/screen_test/settings/align/children_right.frame +1 -1
  95. data/screen_test/settings/align/children_right.rb +1 -1
  96. data/screen_test/settings/align/grid_center.frame +1 -1
  97. data/screen_test/settings/align/grid_center.rb +1 -1
  98. data/screen_test/settings/align/grid_default.frame +1 -1
  99. data/screen_test/settings/align/grid_default.rb +1 -1
  100. data/screen_test/settings/align/grid_left.frame +1 -1
  101. data/screen_test/settings/align/grid_left.rb +1 -1
  102. data/screen_test/settings/align/grid_right.frame +1 -1
  103. data/screen_test/settings/align/grid_right.rb +1 -1
  104. data/screen_test/settings/ansi/bold.frame +1 -1
  105. data/screen_test/settings/ansi/bold.rb +1 -1
  106. data/screen_test/settings/ansi/color.frame +1 -1
  107. data/screen_test/settings/ansi/color.rb +1 -1
  108. data/screen_test/settings/ansi/underline.frame +1 -1
  109. data/screen_test/settings/ansi/underline.rb +1 -1
  110. data/screen_test/settings/border.frame +1 -1
  111. data/screen_test/settings/border.rb +1 -1
  112. data/screen_test/settings/flow/box_b2t.frame +1 -1
  113. data/screen_test/settings/flow/box_b2t.rb +1 -1
  114. data/screen_test/settings/flow/box_l2r.frame +1 -1
  115. data/screen_test/settings/flow/box_l2r.rb +1 -1
  116. data/screen_test/settings/flow/box_r2l.frame +1 -1
  117. data/screen_test/settings/flow/box_r2l.rb +1 -1
  118. data/screen_test/settings/flow/box_t2b.frame +1 -1
  119. data/screen_test/settings/flow/box_t2b.rb +1 -1
  120. data/screen_test/settings/flow/grid_b2t.frame +1 -1
  121. data/screen_test/settings/flow/grid_b2t.rb +2 -2
  122. data/screen_test/settings/flow/grid_l2r.frame +1 -1
  123. data/screen_test/settings/flow/grid_l2r.rb +2 -2
  124. data/screen_test/settings/flow/grid_r2l.frame +1 -1
  125. data/screen_test/settings/flow/grid_r2l.rb +2 -2
  126. data/screen_test/settings/flow/grid_t2b.frame +1 -1
  127. data/screen_test/settings/flow/grid_t2b.rb +2 -2
  128. data/screen_test/settings/height/box.frame +1 -1
  129. data/screen_test/settings/height/box.rb +1 -1
  130. data/screen_test/settings/height/box_border_sizing.frame +1 -1
  131. data/screen_test/settings/height/box_border_sizing.rb +1 -1
  132. data/screen_test/settings/height/grid.frame +1 -1
  133. data/screen_test/settings/height/grid.rb +1 -1
  134. data/screen_test/settings/height/overflow_box.frame +1 -1
  135. data/screen_test/settings/height/overflow_box.rb +1 -1
  136. data/screen_test/settings/height/overflow_box_l2r.frame +1 -1
  137. data/screen_test/settings/height/overflow_box_l2r.rb +1 -1
  138. data/screen_test/settings/height/overflow_box_t2b.frame +1 -1
  139. data/screen_test/settings/height/overflow_box_t2b.rb +1 -1
  140. data/screen_test/settings/height/overflow_grid.frame +1 -1
  141. data/screen_test/settings/height/overflow_grid.rb +1 -1
  142. data/screen_test/settings/margin.frame +1 -1
  143. data/screen_test/settings/margin.rb +1 -1
  144. data/screen_test/settings/padding.frame +1 -1
  145. data/screen_test/settings/padding.rb +1 -1
  146. data/screen_test/settings/position/box_left.frame +1 -1
  147. data/screen_test/settings/position/box_left.rb +1 -1
  148. data/screen_test/settings/position/box_left_negative.frame +1 -1
  149. data/screen_test/settings/position/box_left_negative.rb +1 -1
  150. data/screen_test/settings/position/box_top.frame +1 -1
  151. data/screen_test/settings/position/box_top.rb +1 -1
  152. data/screen_test/settings/position/box_top_negative.frame +1 -1
  153. data/screen_test/settings/position/box_top_negative.rb +1 -1
  154. data/screen_test/settings/position/grid_left.frame +1 -1
  155. data/screen_test/settings/position/grid_left.rb +1 -1
  156. data/screen_test/settings/position/grid_left_negative.frame +1 -1
  157. data/screen_test/settings/position/grid_left_negative.rb +1 -1
  158. data/screen_test/settings/position/grid_top.frame +1 -1
  159. data/screen_test/settings/position/grid_top.rb +1 -1
  160. data/screen_test/settings/position/grid_top_negative.frame +1 -1
  161. data/screen_test/settings/position/grid_top_negative.rb +1 -1
  162. data/screen_test/settings/scroll/horiz_box.frame +1 -1
  163. data/screen_test/settings/scroll/horiz_box.rb +1 -1
  164. data/screen_test/settings/scroll/horiz_box_align_center.rb +1 -1
  165. data/screen_test/settings/scroll/horiz_box_align_right.rb +1 -1
  166. data/screen_test/settings/scroll/vert_box.frame +1 -1
  167. data/screen_test/settings/scroll/vert_box.rb +1 -1
  168. data/screen_test/settings/title_font.frame +1 -1
  169. data/screen_test/settings/title_font.rb +1 -1
  170. data/screen_test/settings/valign/box_around.frame +1 -1
  171. data/screen_test/settings/valign/box_around.rb +1 -1
  172. data/screen_test/settings/valign/box_between.frame +1 -1
  173. data/screen_test/settings/valign/box_between.rb +1 -1
  174. data/screen_test/settings/valign/box_bottom.frame +1 -1
  175. data/screen_test/settings/valign/box_bottom.rb +1 -1
  176. data/screen_test/settings/valign/box_default.frame +1 -1
  177. data/screen_test/settings/valign/box_default.rb +1 -1
  178. data/screen_test/settings/valign/box_evenly.frame +1 -1
  179. data/screen_test/settings/valign/box_evenly.rb +1 -1
  180. data/screen_test/settings/valign/box_middle.frame +1 -1
  181. data/screen_test/settings/valign/box_middle.rb +1 -1
  182. data/screen_test/settings/valign/box_top.frame +1 -1
  183. data/screen_test/settings/valign/box_top.rb +1 -1
  184. data/screen_test/settings/valign/grid_bottom.frame +1 -1
  185. data/screen_test/settings/valign/grid_bottom.rb +1 -1
  186. data/screen_test/settings/valign/grid_default.frame +1 -1
  187. data/screen_test/settings/valign/grid_default.rb +1 -1
  188. data/screen_test/settings/valign/grid_middle.frame +1 -1
  189. data/screen_test/settings/valign/grid_middle.rb +1 -1
  190. data/screen_test/settings/valign/grid_top.frame +1 -1
  191. data/screen_test/settings/valign/grid_top.rb +1 -1
  192. data/screen_test/settings/width/box_border_sizing.frame +1 -1
  193. data/screen_test/settings/width/box_border_sizing.rb +1 -1
  194. data/screen_test/settings/width/box_content.frame +1 -1
  195. data/screen_test/settings/width/box_content.rb +1 -1
  196. data/screen_test/settings/width/box_default.frame +1 -1
  197. data/screen_test/settings/width/box_default.rb +1 -1
  198. data/screen_test/settings/width/grid.frame +1 -1
  199. data/screen_test/settings/width/grid.rb +1 -1
  200. data/screen_test/settings/width/overflow_align_center.frame +1 -1
  201. data/screen_test/settings/width/overflow_align_center.rb +1 -1
  202. data/screen_test/settings/width/overflow_align_right.frame +1 -1
  203. data/screen_test/settings/width/overflow_align_right.rb +1 -1
  204. data/screen_test/settings/width/overflow_box.frame +1 -1
  205. data/screen_test/settings/width/overflow_box.rb +1 -1
  206. data/screen_test/settings/width/overflow_box_l2r.frame +1 -1
  207. data/screen_test/settings/width/overflow_box_l2r.rb +1 -1
  208. data/screen_test/settings/width/overflow_box_t2b.frame +1 -1
  209. data/screen_test/settings/width/overflow_box_t2b.rb +1 -1
  210. data/screen_test/settings/width/overflow_grid.frame +1 -1
  211. data/screen_test/settings/width/overflow_grid.rb +1 -1
  212. data/tools/whirled_peas/tools/screen_tester.rb +131 -65
  213. metadata +42 -9
  214. data/lib/whirled_peas/frame.rb +0 -6
  215. data/lib/whirled_peas/frame/consumer.rb +0 -30
  216. data/lib/whirled_peas/frame/debug_consumer.rb +0 -30
  217. data/lib/whirled_peas/frame/event_loop.rb +0 -90
  218. data/lib/whirled_peas/frame/producer.rb +0 -67
  219. data/lib/whirled_peas/graphics/screen.rb +0 -70
  220. data/lib/whirled_peas/graphics/text_dimensions.rb +0 -15
@@ -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,43 @@
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 LoadError
12
+ puts "Error loading #{config_file}"
13
+ exit(1)
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :config_file
19
+
20
+ def validate!
21
+ super
22
+ # Note that the main script consumes the <command> argument from ARGV, so we
23
+ # expect the config file to be at index 0.
24
+ config_file = args.shift
25
+ if config_file.nil?
26
+ @error_text = "#{command_name} requires a config file"
27
+ elsif !File.exist?(config_file)
28
+ @error_text = "File not found: #{config_file}"
29
+ elsif config_file[-3..-1] != '.rb'
30
+ @error_text = 'Config file should be a .rb file'
31
+ else
32
+ # We think we have a valid ruby config file, set the absolute path to @config
33
+ @config_file = config_file[0] == '/' ? config_file : File.join(Dir.pwd, config_file)
34
+ end
35
+ end
36
+
37
+ def options_usage
38
+ '<config file>'
39
+ end
40
+ end
41
+ private_constant :ConfigCommand
42
+ end
43
+ 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,111 @@
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
+ rescue LoadError
42
+ puts "Error loading #{config_file}"
43
+ exit(1)
44
+ end
45
+
46
+ private
47
+
48
+ attr_reader :app_config_file, :config, :logger
49
+ end
50
+
51
+ class FilePlayer
52
+ def initialize(wpz_file)
53
+ @wpz_file = wpz_file
54
+ end
55
+
56
+ def play
57
+ require 'whirled_peas/device/screen'
58
+ require 'whirled_peas/utils/ansi'
59
+ require 'whirled_peas/utils/file_handler'
60
+
61
+ Utils::Ansi.with_screen do
62
+ screen = Device::Screen.new(WhirledPeas.config.refresh_rate)
63
+ renders = Utils::FileHandler.read(wpz_file)
64
+ screen.handle_renders(renders)
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ attr_reader :wpz_file
71
+ end
72
+
73
+ def self.description
74
+ 'Play an animation from an application or prerecorded file'
75
+ end
76
+
77
+ def start
78
+ super
79
+ player.play
80
+ end
81
+
82
+ private
83
+
84
+ attr_reader :player
85
+
86
+ def validate!
87
+ super
88
+ @player = NullPlayer
89
+ file = args.shift
90
+ if file.nil?
91
+ @error_text = "#{command_name} requires an config file or frames file file"
92
+ elsif !File.exist?(file)
93
+ @error_text = "File not found: #{file}"
94
+ else
95
+ full_path_file = file[0] == '/' ? file : File.join(Dir.pwd, file)
96
+ if full_path_file.end_with?('.wpz')
97
+ @player = FilePlayer.new(full_path_file)
98
+ elsif full_path_file.end_with?('.rb')
99
+ @player = ApplicationPlayer.new(full_path_file, config, build_logger)
100
+ else
101
+ @error_text = "Unsupported file type: .#{file.split('.').last}, epxecting .rb or .wpz"
102
+ end
103
+ end
104
+ end
105
+
106
+ def options_usage
107
+ '<config/wpz file>'
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,57 @@
1
+ require_relative 'config_command'
2
+
3
+ module WhirledPeas
4
+ module Command
5
+ class Record < ConfigCommand
6
+ def self.description
7
+ 'Record animation to a file'
8
+ end
9
+
10
+ def start
11
+ super
12
+ require 'highline'
13
+ require 'whirled_peas/animator/renderer_consumer'
14
+ require 'whirled_peas/animator/producer'
15
+ require 'whirled_peas/device/output_file'
16
+
17
+ width, height = HighLine.new.terminal.terminal_size
18
+ consumer = Animator::RendererConsumer.new(
19
+ WhirledPeas.config.template_factory,
20
+ Device::OutputFile.new(out_file),
21
+ width,
22
+ height
23
+ )
24
+ Animator::Producer.produce(
25
+ consumer, WhirledPeas.config.refresh_rate
26
+ ) do |producer|
27
+ config.application.start(producer)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :out_file
34
+
35
+ def validate!
36
+ super
37
+ return unless @error_text.nil?
38
+
39
+ out_file = args.shift
40
+ if out_file.nil?
41
+ @error_text = "#{command_name} requires an output file"
42
+ elsif !out_file.end_with?('.wpz')
43
+ if out_file.split('/').last =~ /\./
44
+ extra = ", found: .#{out_file.split('.').last}"
45
+ end
46
+ @error_text = "Expecting output file with .wpz extension#{extra}"
47
+ else
48
+ @out_file = out_file[0] == '/' ? out_file : File.join(Dir.pwd, out_file)
49
+ end
50
+ end
51
+
52
+ def options_usage
53
+ [*super, '<output file>'].join(' ')
54
+ end
55
+ end
56
+ end
57
+ end