whirled_peas 0.7.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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