cloudfactory 0.0.13

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 (116) hide show
  1. data/.gitignore +11 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +10 -0
  4. data/README.md +44 -0
  5. data/Rakefile +40 -0
  6. data/bin/cf +8 -0
  7. data/cf.gemspec +76 -0
  8. data/example/google_translate_app/Gemfile +12 -0
  9. data/example/google_translate_app/config.ru +7 -0
  10. data/example/google_translate_app/google_translate_input.csv +4 -0
  11. data/example/google_translate_app/google_translator_app.rb +53 -0
  12. data/example/google_translate_app/views/index.haml +2 -0
  13. data/example/google_translate_app/views/layout.haml +7 -0
  14. data/example/google_translate_app/views/run.haml +4 -0
  15. data/example/google_translate_app/views/style.sass +2 -0
  16. data/example/human_worker_app/Gemfile +12 -0
  17. data/example/human_worker_app/config.ru +7 -0
  18. data/example/human_worker_app/human_worker_app.rb +55 -0
  19. data/example/human_worker_app/human_worker_input.csv +5 -0
  20. data/example/human_worker_app/public/app.js +12 -0
  21. data/example/human_worker_app/temp.csv +3 -0
  22. data/example/human_worker_app/views/index.haml +15 -0
  23. data/example/human_worker_app/views/layout.haml +10 -0
  24. data/example/human_worker_app/views/result.haml +18 -0
  25. data/example/human_worker_app/views/run.haml +12 -0
  26. data/example/human_worker_app/views/style.sass +25 -0
  27. data/example/sample_yaml_files/concept_tagging_robot.yaml +18 -0
  28. data/example/sample_yaml_files/content_scraping_robot.yaml +19 -0
  29. data/example/sample_yaml_files/entity_extraction_robot.yaml +18 -0
  30. data/example/sample_yaml_files/google_translate_robot.yaml +20 -0
  31. data/example/sample_yaml_files/image_processing_robot.yaml +20 -0
  32. data/example/sample_yaml_files/keyword_matching_and_text_extraction_robot.yaml +26 -0
  33. data/example/sample_yaml_files/mailer_robot.yaml +21 -0
  34. data/example/sample_yaml_files/media_converter_robot.yaml +21 -0
  35. data/example/sample_yaml_files/media_splitting_robot.yaml +20 -0
  36. data/example/sample_yaml_files/multiple_skill_badge.yaml +75 -0
  37. data/example/sample_yaml_files/sentiment_robot.yaml +19 -0
  38. data/example/sample_yaml_files/skill_badge.yaml +56 -0
  39. data/example/sample_yaml_files/stat_badge.yaml +40 -0
  40. data/example/sample_yaml_files/term_extraction_robot.yaml +20 -0
  41. data/example/sample_yaml_files/tournament_station_and_form_fields.yaml +40 -0
  42. data/features/form_generation.feature +46 -0
  43. data/features/form_preview.feature +98 -0
  44. data/features/line_creation.feature +99 -0
  45. data/features/line_deletion.feature +50 -0
  46. data/features/line_generation.feature +57 -0
  47. data/features/run.feature +141 -0
  48. data/features/support/cli_steps.rb +16 -0
  49. data/features/support/env.rb +23 -0
  50. data/features/target_url.feature +82 -0
  51. data/fixtures/api_credentials_example.yml +4 -0
  52. data/fixtures/input_data/media_converter_robot.csv +2 -0
  53. data/fixtures/input_data/test.csv +2 -0
  54. data/lib/cf.rb +94 -0
  55. data/lib/cf/account.rb +32 -0
  56. data/lib/cf/cli.rb +52 -0
  57. data/lib/cf/cli/config.rb +87 -0
  58. data/lib/cf/cli/form.rb +82 -0
  59. data/lib/cf/cli/line.rb +237 -0
  60. data/lib/cf/cli/production.rb +62 -0
  61. data/lib/cf/cli/templates/css_file.css.erb +22 -0
  62. data/lib/cf/cli/templates/form_preview.html.erb +17 -0
  63. data/lib/cf/cli/templates/html_file.html.erb +21 -0
  64. data/lib/cf/cli/templates/js_file.js.erb +18 -0
  65. data/lib/cf/cli/templates/line.tt +55 -0
  66. data/lib/cf/cli/templates/sample-line/form.css +27 -0
  67. data/lib/cf/cli/templates/sample-line/form.html +26 -0
  68. data/lib/cf/cli/templates/sample-line/form.js +7 -0
  69. data/lib/cf/cli/templates/sample-line/line.yml.erb +67 -0
  70. data/lib/cf/cli/templates/sample-line/sample-line.csv +3 -0
  71. data/lib/cf/client.rb +56 -0
  72. data/lib/cf/custom_task_form.rb +136 -0
  73. data/lib/cf/department.rb +24 -0
  74. data/lib/cf/final_output.rb +20 -0
  75. data/lib/cf/form_field.rb +62 -0
  76. data/lib/cf/human_worker.rb +67 -0
  77. data/lib/cf/input_format.rb +134 -0
  78. data/lib/cf/line.rb +231 -0
  79. data/lib/cf/robot_worker.rb +31 -0
  80. data/lib/cf/run.rb +158 -0
  81. data/lib/cf/station.rb +340 -0
  82. data/lib/cf/task_form.rb +147 -0
  83. data/lib/cf/version.rb +3 -0
  84. data/lib/generators/cf/form/form_generator.rb +55 -0
  85. data/lib/generators/cf/form/templates/cf_form.html.erb +11 -0
  86. data/lib/generators/cf/install/install_generator.rb +22 -0
  87. data/lib/generators/cf/install/templates/README +13 -0
  88. data/lib/generators/cf/install/templates/cloud_factory.rb +7 -0
  89. data/spec/account_spec.rb +11 -0
  90. data/spec/badge_spec.rb +88 -0
  91. data/spec/concept_tagging_robot_spec.rb +54 -0
  92. data/spec/config_spec.rb +20 -0
  93. data/spec/content_scraping_robot_spec.rb +58 -0
  94. data/spec/custom_task_form_spec.rb +190 -0
  95. data/spec/department_spec.rb +14 -0
  96. data/spec/entity_extraction_robot_spec.rb +56 -0
  97. data/spec/form_field_spec.rb +126 -0
  98. data/spec/generators/form_generator_spec.rb +60 -0
  99. data/spec/generators/install_generator_spec.rb +35 -0
  100. data/spec/google_translate_robot_spec.rb +64 -0
  101. data/spec/human_worker_spec.rb +118 -0
  102. data/spec/image_processing_robot_spec.rb +56 -0
  103. data/spec/input_format_spec.rb +113 -0
  104. data/spec/keyword_matching_and_text_extraction_robot_spec.rb +73 -0
  105. data/spec/line_spec.rb +338 -0
  106. data/spec/mailer_robot_spec.rb +62 -0
  107. data/spec/media_converter_robot_spec.rb +72 -0
  108. data/spec/media_splitting_robot_spec.rb +62 -0
  109. data/spec/run_spec.rb +298 -0
  110. data/spec/sentiment_robot_spec.rb +56 -0
  111. data/spec/spec_helper.rb +39 -0
  112. data/spec/station_spec.rb +256 -0
  113. data/spec/task_form_spec.rb +96 -0
  114. data/spec/term_extraction_robot_spec.rb +58 -0
  115. data/spec/text_appending_robot_spec.rb +86 -0
  116. metadata +472 -0
@@ -0,0 +1,16 @@
1
+ Then /^I should see "([^"]*)"$/ do |expected|
2
+ assert_partial_output(expected, all_output)
3
+ end
4
+
5
+ When /^I type "([^"]*)" and press Enter$/ do |text|
6
+ type(text)
7
+ end
8
+
9
+ Then /^I debug$/ do
10
+ breakpoint
11
+ 0
12
+ end
13
+
14
+ Then /^the file "([^"]*)" should contain:$/ do |file, partial_content|
15
+ check_file_content(file, partial_content, true)
16
+ end
@@ -0,0 +1,23 @@
1
+ require 'ruby-debug'
2
+ require 'aruba/cucumber'
3
+ require 'cf'
4
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
5
+
6
+ Before('@slow_process') do
7
+ @aruba_io_wait_seconds = 4
8
+ @aruba_timeout_seconds = 4
9
+ end
10
+
11
+ Before('@too_slow_process') do
12
+ @aruba_io_wait_seconds = 4
13
+ @aruba_timeout_seconds = 50
14
+ end
15
+
16
+ if ENV['TEST_CLI']
17
+ API_CONFIG = YAML.load_file(File.expand_path("../../../fixtures/api_credentials.yml", __FILE__))
18
+ CF.configure do |config|
19
+ config.api_version = API_CONFIG['api_version']
20
+ config.api_url = API_CONFIG['api_url']
21
+ config.api_key = API_CONFIG['api_key']
22
+ end
23
+ end
@@ -0,0 +1,82 @@
1
+ Feature: Target URL
2
+ In order to talk with cloud factory
3
+ As a CLI user
4
+ I want to setup login credentials
5
+
6
+ Scenario: Setting up staging target uri
7
+ When I run `cf target staging`
8
+ Then the file ".cf_credentials" should contain exactly:
9
+ """
10
+ ---
11
+ :target_url: http://sandbox.staging.cloudfactory.com/api/
12
+ :api_version: v1
13
+
14
+ """
15
+ Then the output should match:
16
+ """
17
+
18
+ Your cloudfactory target url is saved as http://sandbox.staging.cloudfactory.com/api/
19
+ All the best to run your factory on top of CloudFactory.com
20
+
21
+ """
22
+
23
+ Scenario: Setting up development target uri
24
+ When I run `cf target development`
25
+ Then the file ".cf_credentials" should contain exactly:
26
+ """
27
+ ---
28
+ :target_url: http://lvh.me:3000/api/
29
+ :api_version: v1
30
+
31
+ """
32
+ Then the output should match:
33
+ """
34
+
35
+ Your cloudfactory target url is saved as http://lvh.me:3000/api/
36
+ All the best to run your factory on top of CloudFactory.com
37
+
38
+ """
39
+
40
+ Scenario: Setting up production target uri
41
+ When I run `cf target production`
42
+ Then the file ".cf_credentials" should contain exactly:
43
+ """
44
+ ---
45
+ :target_url: http://sandbox.cloudfactory.com/api/
46
+ :api_version: v1
47
+
48
+ """
49
+ Then the output should match:
50
+ """
51
+
52
+ Your cloudfactory target url is saved as http://sandbox.cloudfactory.com/api/
53
+ All the best to run your factory on top of CloudFactory.com
54
+
55
+ """
56
+
57
+ Scenario: Show the current target if no param is passed
58
+ Given a file named ".cf_credentials" with:
59
+ """
60
+ ---
61
+ :target_url: http://sandbox.staging.cloudfactory.com/api/
62
+ :api_version: v1
63
+
64
+ """
65
+ When I run `cf target`
66
+ Then the output should match:
67
+ """
68
+
69
+ http://sandbox.staging.cloudfactory.com/api/
70
+
71
+ """
72
+
73
+ Scenario: Show the message to set the target uri if no param is passed and the .cf_credentials file does not exist
74
+ When I run `cf target`
75
+ Then the output should match:
76
+ """
77
+
78
+ You have not set the target url yet.
79
+ Set the target uri with: cf target --url=http://sandbox.staging.cloudfactory.com
80
+
81
+ """
82
+
@@ -0,0 +1,4 @@
1
+ account_name: "manish"
2
+ api_key: "f488a62d0307e79ec4f1e6131fa220be47e83d44"
3
+ api_version: "v1"
4
+ api_url: "http://manish.lvh.me:3000/api/"
@@ -0,0 +1,2 @@
1
+ url_1
2
+ http://media-robot.s3.amazonaws.com/media_robot/p.mp3
@@ -0,0 +1,2 @@
1
+ Company,Website,meta_data_company
2
+ Apple,apple.com,Apple
data/lib/cf.rb ADDED
@@ -0,0 +1,94 @@
1
+ begin
2
+ require 'psych'
3
+ rescue LoadError
4
+ # do nothing
5
+ end
6
+ require 'yaml'
7
+ require 'rails'
8
+ require 'hashie'
9
+ require 'active_support/concern'
10
+ #require 'active_support/rescuable'
11
+ require 'active_support/core_ext/string/inflections'
12
+ require 'active_support/core_ext/object/blank'
13
+ require 'active_support/core_ext/object/try'
14
+ require "rest_client"
15
+ require 'json'
16
+ require 'terminal-table/import'
17
+
18
+ directory = File.expand_path(File.dirname(__FILE__))
19
+
20
+ Hash.send :include, Hashie::HashExtensions
21
+
22
+
23
+ module CF
24
+
25
+ class << self
26
+ attr_accessor :api_key, :account_name, :api_version, :api_url
27
+ def configure
28
+ yield self
29
+ end
30
+ end
31
+
32
+ # FIX: raise the exception along with the response error message
33
+ # Ref: http://www.simonecarletti.com/blog/2009/12/inside-ruby-on-rails-rescuable-and-rescue_from/
34
+ # class CFError < StandardError
35
+ #
36
+ # include ActiveSupport::Rescuable
37
+ #
38
+ # attr_reader :data
39
+ # rescue_from NotFound, :with => :render_error
40
+ #
41
+ # def initialize(data)
42
+ # @data = data
43
+ # # debugger
44
+ # super
45
+ # end
46
+ #
47
+ # def to_s
48
+ # "Error: #{@data}"
49
+ # end
50
+ #
51
+ # def render_error(exception)
52
+ # @error = exception
53
+ # "Error: #{exception.class}: #{exception.message}"
54
+ # debugger
55
+ # puts ""
56
+ # end
57
+ #
58
+ # end
59
+ #
60
+ #
61
+ # class ClientError < StandardError; end
62
+ # class ServerError < CFError; end
63
+ # class General < CFError; end
64
+ #
65
+ class CFError < StandardError; end
66
+ class ImproveStationNotAllowed < CFError; end
67
+ #
68
+ # class Unauthorized < ClientError; end
69
+ # class NotFound < ClientError
70
+ # # attr_accessor :data
71
+ # # def initialize(data)
72
+ # #
73
+ # # @data = data
74
+ # # debugger
75
+ # # puts ""
76
+ # # end
77
+ # end
78
+ #
79
+ # class Unavailable < StandardError; end
80
+ end
81
+
82
+ require "#{directory}/cf/client"
83
+ require "#{directory}/cf/account"
84
+ require "#{directory}/cf/line"
85
+ require "#{directory}/cf/input_format"
86
+ require "#{directory}/cf/station"
87
+ require "#{directory}/cf/human_worker"
88
+ require "#{directory}/cf/task_form"
89
+ require "#{directory}/cf/form_field"
90
+ require "#{directory}/cf/custom_task_form"
91
+ require "#{directory}/cf/run"
92
+ require "#{directory}/cf/department"
93
+ require "#{directory}/cf/final_output"
94
+ require "#{directory}/cf/robot_worker"
data/lib/cf/account.rb ADDED
@@ -0,0 +1,32 @@
1
+ module CF
2
+ class Account
3
+ include Client
4
+
5
+ class << self
6
+ attr_accessor :errors
7
+
8
+ def info
9
+ resp = get('/account.json')
10
+
11
+ if resp.code != 200
12
+ self.errors = resp.error.message
13
+ end
14
+ return resp
15
+ end
16
+
17
+ def valid?
18
+ info
19
+ errors.nil?
20
+ end
21
+
22
+ def login(email, passwd)
23
+ resp = post('/api_login.json', {:email => email, :password => passwd})
24
+
25
+ if resp.code != 200
26
+ self.errors = resp.error.message
27
+ end
28
+ return resp
29
+ end
30
+ end
31
+ end
32
+ end
data/lib/cf/cli.rb ADDED
@@ -0,0 +1,52 @@
1
+ begin
2
+ require 'psych'
3
+ rescue LoadError
4
+ # do nothing
5
+ end
6
+ require 'yaml'
7
+ require 'fileutils'
8
+ require 'thor'
9
+
10
+ require File.expand_path('../../cf', __FILE__) #=> requiring the gem
11
+ require 'active_support/core_ext/string/inflections'
12
+ require 'active_support/core_ext/object/blank'
13
+
14
+ cli_directory = File.expand_path("../cf/cli", File.dirname(__FILE__))
15
+ require "#{cli_directory}/config"
16
+ require "#{cli_directory}/line"
17
+ require "#{cli_directory}/form"
18
+ require "#{cli_directory}/production"
19
+
20
+ module Cf
21
+ class CLI < Thor
22
+ include Thor::Actions
23
+ include Cf::Config
24
+
25
+ desc "target", "Setup the cloudfactory credentials. e.g. cf target staging #=> http://sandbox.staging.cloudfactory.com (options: staging/development/production)"
26
+ def target(target_url=nil)
27
+ if target_url.present?
28
+ target_set_url = save_config(target_url)
29
+ say("\nYour cloudfactory target url is saved as #{target_set_url}", :green)
30
+ say("All the best to run your factory on top of CloudFactory.com\n", :green)
31
+ else
32
+ if load_config
33
+ say("\n#{load_config[:target_url]}\n", :green)
34
+ else
35
+ say("\nYou have not set the target url yet.", :yellow)
36
+ say("Set the target uri with: cf target --url=http://sandbox.staging.cloudfactory.com\n", :yellow)
37
+ end
38
+ end
39
+ end
40
+
41
+ desc "line", "Commands to manage the Lines. For more info, cf line help"
42
+ subcommand "line", Cf::Line
43
+
44
+ desc "form", "Commands to generate custom task forms. For more info, cf form help"
45
+ subcommand "form", Cf::Form
46
+
47
+ desc "production", "Commands to create production runs. For more info, cf production help"
48
+ # cannot use Run for the class name coz its a reserved word for Thor
49
+ # later it can be replaced with hacked millisami-thor version of the thor library with run-time dependency via Bundler
50
+ subcommand "production", Cf::Production
51
+ end
52
+ end
@@ -0,0 +1,87 @@
1
+ module Cf
2
+ module Config
3
+ def config_file
4
+ File.join(find_home, '.cf_credentials')
5
+ end
6
+
7
+ def load_config
8
+ YAML::load(File.read(config_file)) if File.exist?(config_file)
9
+ end
10
+
11
+ def save_config(target_url)
12
+ if target_url == "staging"
13
+ target_set_url = "http://sandbox.staging.cloudfactory.com/api/"
14
+ elsif target_url == "development"
15
+ target_set_url = "http://lvh.me:3000/api/"
16
+ elsif target_url == "production"
17
+ target_set_url = "http://sandbox.cloudfactory.com/api/"
18
+ end
19
+ File.open(config_file, 'w') {|f| f.write({ :target_url => "#{target_set_url}", :api_version => "v1" }.to_yaml) }
20
+ return target_set_url
21
+ end
22
+
23
+ def set_target_uri(live)
24
+ if load_config
25
+ CF.api_url = load_config[:target_url]
26
+ CF.api_version = load_config[:api_version]
27
+ else
28
+ CF.api_url = "http://sandbox.cloudfactory.com/api"
29
+ CF.api_version = "v1"
30
+ end
31
+
32
+ if live
33
+ if CF.api_url == "http://sandbox.staging.cloudfactory.com/api"
34
+ CF.api_url = "http://staging.cloudfactory.com/api"
35
+ end
36
+
37
+ if CF.api_url == "http://sandbox.cloudfactory.com/api"
38
+ CF.api_url = "http://cloudfactory.com/api"
39
+ end
40
+ end
41
+ end
42
+
43
+ def get_api_key(line_yaml_file)
44
+ line_yml = YAML::load(File.read(line_yaml_file))
45
+ line_yml['api_key'].presence
46
+ end
47
+
48
+ def set_api_key(yaml_source)
49
+ # debugger
50
+ api_key = get_api_key(yaml_source)
51
+ if api_key.blank?
52
+ return false
53
+ else
54
+ CF.api_key = api_key if CF.api_key.blank?
55
+ return true
56
+ end
57
+ end
58
+
59
+ # Ripped from rubygems
60
+ unless ENV['TEST_CLI']
61
+ def find_home
62
+ unless RUBY_VERSION > '1.9' then
63
+ ['HOME', 'USERPROFILE'].each do |homekey|
64
+ return File.expand_path(ENV[homekey]) if ENV[homekey]
65
+ end
66
+
67
+ if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
68
+ return File.expand_path("#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}")
69
+ end
70
+ end
71
+
72
+ File.expand_path "~"
73
+ rescue
74
+ if File::ALT_SEPARATOR then
75
+ drive = ENV['HOMEDRIVE'] || ENV['SystemDrive']
76
+ File.join(drive.to_s, '/')
77
+ else
78
+ "/"
79
+ end
80
+ end
81
+ else
82
+ def find_home
83
+ File.expand_path(File.dirname(__FILE__) + '/../../../tmp/aruba')
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,82 @@
1
+ require 'thor/group'
2
+
3
+ module Cf
4
+ class Newform < Thor::Group
5
+ include Thor::Actions
6
+ include Cf::Config
7
+ source_root File.expand_path('../templates', __FILE__)
8
+ argument :station, :type => :numeric
9
+ argument :labels, :type => :string
10
+ argument :fields, :type => :hash
11
+
12
+ def generate_form_template
13
+ line_destination = Dir.pwd
14
+ template("html_file.html.erb", "#{line_destination}/station_#{station}/form.html")
15
+ template("css_file.css.erb", "#{line_destination}/station_#{station}/form.css")
16
+ template("js_file.js.erb", "#{line_destination}/station_#{station}/form.js")
17
+ end
18
+ end
19
+
20
+ class FormPreview < Thor::Group
21
+ include Thor::Actions
22
+ include Cf::Config
23
+ source_root File.expand_path('../templates', __FILE__)
24
+ argument :station, :type => :numeric
25
+ argument :form_content, :type => :string
26
+
27
+ def generate_form_preview
28
+ line_destination = Dir.pwd
29
+ template("form_preview.html.erb", "#{line_destination}/station_#{station}/form_preview.html")
30
+ end
31
+
32
+ def launch_preview
33
+ line_destination = Dir.pwd
34
+ system "open #{line_destination}/station_#{station}/form_preview.html"
35
+ end
36
+ end
37
+ end
38
+
39
+ module Cf
40
+ class Form < Thor
41
+ include Cf::Config
42
+
43
+ desc "form generate", "generates a custom task form at <line-title>/<form-title>.html and its associated css and js files"
44
+ method_option :station, :type => :numeric, :required => true, :aliases => "-st", :desc => "the station index this form should be associated with"
45
+ method_option :labels, :type => :string, :required => true, :aliases => "-lb", :desc => "the labels that will be shown to the worker on MTurk window"
46
+ method_option :fields, :type => :hash, :required => true, :aliases => "-fd", :desc => "the actual form fields that the worker will fill in"
47
+ method_option :force, :type => :boolean, :default => false, :aliases => "-f", :desc => "force to overwrite the files if the form already exists, default is false"
48
+
49
+ def generate
50
+ line_destination = Dir.pwd
51
+ unless File.exist?("#{line_destination}/line.yml")
52
+ say("The current directory is not a valid line directory.", :red) and return
53
+ end
54
+
55
+ FileUtils.rm_rf("#{line_destination}/station_#{options[:station]}", :verbose => true) if options.force? && Dir.exist?("#{line_destination}/station_#{options[:station]}")
56
+ if Dir.exist?("#{line_destination}/station_#{options[:station]}")
57
+ say "Skipping the form generation because the station_#{options[:station]} already exists with its custom form.\nUse the -f flag to force it to overwrite or check and delete the station_#{options[:station]} folder manually.", :red
58
+ else
59
+ say "Generating form for station #{options[:station]}", :green
60
+ Cf::Newform.start([options[:station], options[:labels], options[:fields]])
61
+ say "A new custom task form created in station_#{options[:station]}."
62
+ end
63
+ end
64
+
65
+ desc "form preview", "generates a html file to preview the custom task form"
66
+ method_option :station, :type => :numeric, :required => true, :aliases => "-s", :desc => "station index of the form to preview"
67
+ def preview
68
+ line_destination = Dir.pwd
69
+ unless File.exist?("#{line_destination}/line.yml")
70
+ say("The current directory is not a valid line directory.", :red) and return
71
+ end
72
+ if Dir.exist?("#{line_destination}/station_#{options[:station]}") and !Dir["#{line_destination}/station_#{options[:station]}/*"].empty?
73
+ say "Generating preview form for station #{options[:station]}", :green
74
+ form_content = File.read("station_#{options[:station]}/form.html")
75
+ Cf::FormPreview.start([options[:station], form_content])
76
+ else
77
+ say "No form exists for station #{options[:station]}", :red
78
+ say "Generate the form for station 2 and then preview it.", :red
79
+ end
80
+ end
81
+ end
82
+ end