From ae8618ee1db0f53fdca69250d6ff4050031a987a Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Fri, 15 Apr 2016 15:21:07 -0700 Subject: [PATCH 01/16] Updating gems --- Gemfile.lock | 87 ++++++++++++++++++++++--------------------- factor.gemspec | 18 ++++----- lib/factor/version.rb | 2 +- 3 files changed, 55 insertions(+), 52 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 79f5bcc..d475215 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,12 +1,12 @@ PATH remote: . specs: - factor (2.9.08) - commander (~> 4.3, >= 4.3.4) - configatron (~> 4.5, >= 4.5.0) - rainbow (~> 2.0, >= 2.0.0) - rest-client (~> 1.8, >= 1.8.0) - rspec (~> 3.3, >= 3.3.0) + factor (3.0.0) + commander (~> 4.4.0) + configatron (~> 4.5.0) + rainbow (~> 2.1.0) + rest-client (~> 1.8.0) + rspec (~> 3.4.0) varify (~> 0.0.5) wrong (~> 0.7.1) @@ -15,25 +15,25 @@ GEM specs: celluloid (0.16.0) timers (~> 4.0.0) - coderay (1.1.0) - commander (4.3.4) + coderay (1.1.1) + commander (4.4.0) highline (~> 1.7.2) configatron (4.5.0) - coveralls (0.8.1) + coveralls (0.8.13) json (~> 1.8) - rest-client (>= 1.6.8, < 2) - simplecov (~> 0.10.0) + simplecov (~> 0.11.0) term-ansicolor (~> 1.3) thor (~> 0.19.1) + tins (~> 1.6.0) diff-lcs (1.2.5) docile (1.1.5) - domain_name (0.5.24) + domain_name (0.5.20160309) unf (>= 0.0.5, < 1.0.0) ffi (1.9.8) formatador (0.2.5) - guard (2.12.6) + guard (2.13.0) formatador (>= 0.2.4) - listen (~> 2.7) + listen (>= 2.7, <= 4.0) lumberjack (~> 1.0) nenv (~> 0.1) notiffany (~> 0.0) @@ -41,12 +41,12 @@ GEM shellany (~> 0.0) thor (>= 0.18.1) guard-compat (1.2.1) - guard-rspec (4.5.2) + guard-rspec (4.6.5) guard (~> 2.1) guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) - highline (1.7.2) - hitimes (1.2.2) + highline (1.7.8) + hitimes (1.2.3) http-cookie (1.0.2) domain_name (~> 0.5) json (1.8.3) @@ -56,19 +56,19 @@ GEM rb-inotify (>= 0.9) lumberjack (1.0.9) method_source (0.8.2) - mime-types (2.6.1) + mime-types (2.99.1) nenv (0.2.0) - netrc (0.10.3) + netrc (0.11.0) notiffany (0.0.6) nenv (~> 0.1) shellany (~> 0.0) predicated (0.2.6) - pry (0.10.1) + pry (0.10.3) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - rainbow (2.0.0) - rake (10.4.2) + rainbow (2.1.0) + rake (11.1.2) rb-fsevent (0.9.5) rb-inotify (0.9.5) ffi (>= 0.5.0) @@ -76,27 +76,27 @@ GEM http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 3.0) netrc (~> 0.7) - rspec (3.3.0) - rspec-core (~> 3.3.0) - rspec-expectations (~> 3.3.0) - rspec-mocks (~> 3.3.0) - rspec-core (3.3.0) - rspec-support (~> 3.3.0) - rspec-expectations (3.3.0) + rspec (3.4.0) + rspec-core (~> 3.4.0) + rspec-expectations (~> 3.4.0) + rspec-mocks (~> 3.4.0) + rspec-core (3.4.4) + rspec-support (~> 3.4.0) + rspec-expectations (3.4.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.3.0) - rspec-mocks (3.3.0) + rspec-support (~> 3.4.0) + rspec-mocks (3.4.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.3.0) - rspec-support (3.3.0) + rspec-support (~> 3.4.0) + rspec-support (3.4.1) ruby2ruby (2.2.0) ruby_parser (~> 3.1) sexp_processor (~> 4.0) - ruby_parser (3.7.0) + ruby_parser (3.8.1) sexp_processor (~> 4.1) - sexp_processor (4.6.0) + sexp_processor (4.7.0) shellany (0.0.1) - simplecov (0.10.0) + simplecov (0.11.2) docile (~> 1.1.0) json (~> 1.8) simplecov-html (~> 0.10.0) @@ -107,10 +107,10 @@ GEM thor (0.19.1) timers (4.0.1) hitimes - tins (1.5.2) + tins (1.6.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.1) + unf_ext (0.0.7.2) varify (0.0.5) wrong (0.7.1) diff-lcs (~> 1.2.5) @@ -123,8 +123,11 @@ PLATFORMS ruby DEPENDENCIES - coveralls (~> 0.8.1) + coveralls (~> 0.8.13) factor! - guard (~> 2.12.6) - guard-rspec (~> 4.5.2) - rake (~> 10.4.2) + guard (~> 2.13.0) + guard-rspec (~> 4.6.5) + rake (~> 11.1.2) + +BUNDLED WITH + 1.11.2 diff --git a/factor.gemspec b/factor.gemspec index 980b960..19e81b4 100644 --- a/factor.gemspec +++ b/factor.gemspec @@ -18,15 +18,15 @@ Gem::Specification.new do |s| s.executables = ['factor'] s.require_paths = ['lib'] - s.add_runtime_dependency 'commander', '~> 4.3', '>= 4.3.4' - s.add_runtime_dependency 'rainbow', '~> 2.0', '>= 2.0.0' - s.add_runtime_dependency 'configatron', '~> 4.5', '>= 4.5.0' - s.add_runtime_dependency 'rest-client', '~> 1.8', '>= 1.8.0' + s.add_runtime_dependency 'commander', '~> 4.4.0' + s.add_runtime_dependency 'rainbow', '~> 2.1.0' + s.add_runtime_dependency 'configatron', '~> 4.5.0' + s.add_runtime_dependency 'rest-client', '~> 1.8.0' s.add_runtime_dependency 'wrong', '~> 0.7.1' - s.add_runtime_dependency 'rspec', '~> 3.3', '>= 3.3.0' + s.add_runtime_dependency 'rspec', '~> 3.4.0' s.add_runtime_dependency 'varify', '~> 0.0.5' - s.add_development_dependency 'coveralls', '~> 0.8.1' - s.add_development_dependency 'rake', '~> 10.4.2' - s.add_development_dependency 'guard', '~> 2.12.6' - s.add_development_dependency 'guard-rspec', '~> 4.5.2' + s.add_development_dependency 'coveralls', '~> 0.8.13' + s.add_development_dependency 'rake', '~> 11.1.2' + s.add_development_dependency 'guard', '~> 2.13.0' + s.add_development_dependency 'guard-rspec', '~> 4.6.5' end diff --git a/lib/factor/version.rb b/lib/factor/version.rb index 5584c93..ee06cf7 100644 --- a/lib/factor/version.rb +++ b/lib/factor/version.rb @@ -2,5 +2,5 @@ # Primary Factor.io module module Factor - VERSION = '2.9.08' + VERSION = '3.0.0' end From 4981f5306159760a5b8cb2ab5f0c10919ee94b81 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Fri, 15 Apr 2016 15:22:27 -0700 Subject: [PATCH 02/16] Don't need these frameworks to do testing anymore --- lib/factor/connector/test.rb | 191 ----------------------------------- lib/factor/workflow/test.rb | 54 ---------- 2 files changed, 245 deletions(-) delete mode 100644 lib/factor/connector/test.rb delete mode 100644 lib/factor/workflow/test.rb diff --git a/lib/factor/connector/test.rb b/lib/factor/connector/test.rb deleted file mode 100644 index c73f93c..0000000 --- a/lib/factor/connector/test.rb +++ /dev/null @@ -1,191 +0,0 @@ -require 'rspec' -require 'rspec/expectations' -require 'rspec/matchers' -require 'wrong' - -module Factor - module Connector - module Test - DEFAULT_DELAY = 5 - DEFAULT_TIMEOUT = 0.25 - - @timeout = DEFAULT_TIMEOUT - @delay = DEFAULT_DELAY - - def self.timeout - @timeout - end - - def self.timeout=(value) - @timeout=value - end - - def self.reset - @timeout = DEFAULT_TIMEOUT - @delay = DEFAULT_DELAY - end - - def self.delay - @delay - end - - def self.delay=(value) - @delay = value - end - - def self.eventually_options - options = {} - options[:timeout] = Factor::Connector::Test.timeout if Factor::Connector::Test.timeout!=DEFAULT_TIMEOUT - options[:delay] = Factor::Connector::Test.delay if Factor::Connector::Test.delay!=DEFAULT_DELAY - options - end - - RSpec::Matchers.define :message do |expected| - match do |actual| - begin - Wrong.eventually(Factor::Connector::Test.eventually_options) do - actual.logs.any? do |log| - case expected.class.name - when 'Hash' - status = expected.keys.first.to_s - message = expected.values.first - log[:type]=='log' && log[:status]==status && log[:message]==message - when 'String' - log[:type]=='log' && log[:message]==expected - when 'Symbol' - log[:type]=='log' && log[:status]==expected.to_s - when 'NilClass' - log[:type]=='log' - else - false - end - end - end - true - rescue => ex - false - end - end - - failure_message do - case expected.class.name - when 'Hash' - status = expected.keys.first.to_s - message = expected.values.first - "expected #{actual.logs} to log '#{status}' message '#{message}'" - when 'Symbol' - "expected #{actual.logs} to log '#{expected}'" - when 'String' - "expected #{actual.logs} to log message '#{expected}'" - when 'NilClass' - "expected #{actual.logs} to log a message" - else - "#{expected.class} is an unrecognizable matcher type" - end - end - end - - RSpec::Matchers.define :respond do |expected| - - match do |actual| - begin - Wrong.eventually(Factor::Connector::Test.eventually_options) do - actual.logs.any? do |log| - case expected.class.name - when 'Hash' - log[:type]=='response' && log[:data]==expected - when 'NilClass' - log[:type]=='response' - else - false - end - end - end - true - rescue => ex - false - end - end - - failure_message do - case expected.class.name - when 'Hash' - "expected #{actual.logs} to respond with data '#{expected}'" - when 'NilClass' - "expected #{actual.logs} to respond" - else - "#{expected.class} is an unrecognizable matcher type" - end - end - end - - RSpec::Matchers.define :trigger do |expected| - - match do |actual| - begin - Wrong.eventually(Factor::Connector::Test.eventually_options) do - actual.logs.any? do |log| - case expected.class.name - when 'Hash' - log[:type]=='trigger' && log[:data]==expected - when 'NilClass' - log[:type]=='trigger' - else - false - end - end - end - true - rescue => ex - false - end - end - - failure_message do - case expected.class.name - when 'Hash' - "expected #{actual.logs} to respond with data '#{expected}'" - when 'NilClass' - "expected #{actual.logs} to respond" - else - "#{expected.class} is an unrecognizable matcher type" - end - end - end - - RSpec::Matchers.define :fail do |expected| - - match do |actual| - begin - Wrong.eventually(Factor::Connector::Test.eventually_options) do - actual.logs.any? do |log| - case expected.class.name - when 'String' - log[:type]=='fail' && log[:message]==expected - when 'NilClass' - log[:type]=='fail' - else - false - end - end - end - true - rescue => ex - false - end - end - - failure_message do - case expected.class.name - when 'String' - "expected #{actual.logs} to fail with message '#{expected}'" - when 'NilClass' - "expected #{actual.logs} to fail" - else - "#{expected.class} is an unrecognizable matcher type" - end - end - end - end - end -end \ No newline at end of file diff --git a/lib/factor/workflow/test.rb b/lib/factor/workflow/test.rb deleted file mode 100644 index 3372ec4..0000000 --- a/lib/factor/workflow/test.rb +++ /dev/null @@ -1,54 +0,0 @@ -require 'rspec' -require 'rspec/expectations' -require 'rspec/matchers' -require 'wrong' - -module Factor - module Workflow - module Test - - RSpec::Matchers.define :log do |expected| - match do |actual| - begin - Wrong.eventually do - actual.history.any? do |log| - case expected.class.name - when 'Hash' - status = expected.keys.first.to_s - message = expected.values.first - status_match = log[:status].to_s == status.to_s - message_match = log[:message].end_with? message - status_match && message_match - when 'String' - log[:message].end_with? expected - when 'Symbol' - log[:status].to_s == expected.to_s - else - false - end - end - end - true - rescue => ex - false - end - end - - failure_message do - case expected.class.name - when 'Hash' - status = expected.keys.first.to_s - message = expected.values.first - "expected #{actual.history} to log '#{status}' message '#{message}'" - when 'Symbol' - "expected #{actual.history} to log '#{expected}'" - when 'String' - "expected #{actual.history} to log message '#{expected}'" - else - "#{expected.class} is an unrecognizable matcher type" - end - end - end - end - end -end \ No newline at end of file From e794bd83dddd56979b3dc23df5cad890dc45577a Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Fri, 15 Apr 2016 23:52:21 -0700 Subject: [PATCH 03/16] MVP functionality for new core --- Gemfile.lock | 33 +---- factor.gemspec | 5 +- lib/commands.rb | 15 +- lib/factor/commands/base.rb | 55 ++++---- lib/factor/commands/run_command.rb | 51 +++---- lib/factor/commands/workflow_command.rb | 78 ++--------- lib/factor/common/blocker.rb | 44 ------ lib/factor/common/deep_struct.rb | 56 -------- lib/factor/connector.rb | 40 ++++++ lib/factor/connector/definition.rb | 124 ----------------- lib/factor/connector/error.rb | 14 -- lib/factor/connector/registry.rb | 22 --- lib/factor/connector/runtime.rb | 97 ------------- lib/factor/logger.rb | 69 ++++++++++ lib/factor/logger/basic.rb | 69 ---------- lib/factor/logger/logger.rb | 28 ---- lib/factor/logger/test.rb | 38 ------ lib/factor/workflow/connector_future.rb | 27 ++++ lib/factor/workflow/definition.rb | 174 ------------------------ lib/factor/workflow/dsl.rb | 34 +++++ lib/factor/workflow/exec_handler.rb | 16 --- lib/factor/workflow/future.rb | 74 ++++++++++ lib/factor/workflow/runtime.rb | 19 +-- lib/factor/workflow/service_address.rb | 50 ------- spec/common/deep_struct_spec.rb | 51 ------- 25 files changed, 308 insertions(+), 975 deletions(-) delete mode 100644 lib/factor/common/blocker.rb delete mode 100644 lib/factor/common/deep_struct.rb create mode 100644 lib/factor/connector.rb delete mode 100644 lib/factor/connector/definition.rb delete mode 100644 lib/factor/connector/error.rb delete mode 100644 lib/factor/connector/registry.rb delete mode 100644 lib/factor/connector/runtime.rb create mode 100644 lib/factor/logger.rb delete mode 100644 lib/factor/logger/basic.rb delete mode 100644 lib/factor/logger/logger.rb delete mode 100644 lib/factor/logger/test.rb create mode 100644 lib/factor/workflow/connector_future.rb delete mode 100644 lib/factor/workflow/definition.rb create mode 100644 lib/factor/workflow/dsl.rb delete mode 100644 lib/factor/workflow/exec_handler.rb create mode 100644 lib/factor/workflow/future.rb delete mode 100644 lib/factor/workflow/service_address.rb delete mode 100644 spec/common/deep_struct_spec.rb diff --git a/Gemfile.lock b/Gemfile.lock index d475215..1860aba 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,12 +3,9 @@ PATH specs: factor (3.0.0) commander (~> 4.4.0) + concurrent-ruby (~> 1.0.1) configatron (~> 4.5.0) rainbow (~> 2.1.0) - rest-client (~> 1.8.0) - rspec (~> 3.4.0) - varify (~> 0.0.5) - wrong (~> 0.7.1) GEM remote: https://rubygems.org/ @@ -18,6 +15,7 @@ GEM coderay (1.1.1) commander (4.4.0) highline (~> 1.7.2) + concurrent-ruby (1.0.1) configatron (4.5.0) coveralls (0.8.13) json (~> 1.8) @@ -27,8 +25,6 @@ GEM tins (~> 1.6.0) diff-lcs (1.2.5) docile (1.1.5) - domain_name (0.5.20160309) - unf (>= 0.0.5, < 1.0.0) ffi (1.9.8) formatador (0.2.5) guard (2.13.0) @@ -47,8 +43,6 @@ GEM rspec (>= 2.99.0, < 4.0) highline (1.7.8) hitimes (1.2.3) - http-cookie (1.0.2) - domain_name (~> 0.5) json (1.8.3) listen (2.10.0) celluloid (~> 0.16.0) @@ -56,13 +50,10 @@ GEM rb-inotify (>= 0.9) lumberjack (1.0.9) method_source (0.8.2) - mime-types (2.99.1) nenv (0.2.0) - netrc (0.11.0) notiffany (0.0.6) nenv (~> 0.1) shellany (~> 0.0) - predicated (0.2.6) pry (0.10.3) coderay (~> 1.1.0) method_source (~> 0.8.1) @@ -72,10 +63,6 @@ GEM rb-fsevent (0.9.5) rb-inotify (0.9.5) ffi (>= 0.5.0) - rest-client (1.8.0) - http-cookie (>= 1.0.2, < 2.0) - mime-types (>= 1.16, < 3.0) - netrc (~> 0.7) rspec (3.4.0) rspec-core (~> 3.4.0) rspec-expectations (~> 3.4.0) @@ -89,12 +76,6 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.4.0) rspec-support (3.4.1) - ruby2ruby (2.2.0) - ruby_parser (~> 3.1) - sexp_processor (~> 4.0) - ruby_parser (3.8.1) - sexp_processor (~> 4.1) - sexp_processor (4.7.0) shellany (0.0.1) simplecov (0.11.2) docile (~> 1.1.0) @@ -108,16 +89,6 @@ GEM timers (4.0.1) hitimes tins (1.6.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.2) - varify (0.0.5) - wrong (0.7.1) - diff-lcs (~> 1.2.5) - predicated (~> 0.2.6) - ruby2ruby (>= 2.0.1) - ruby_parser (>= 3.0.1) - sexp_processor (>= 4.0) PLATFORMS ruby diff --git a/factor.gemspec b/factor.gemspec index 19e81b4..72225f3 100644 --- a/factor.gemspec +++ b/factor.gemspec @@ -21,10 +21,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'commander', '~> 4.4.0' s.add_runtime_dependency 'rainbow', '~> 2.1.0' s.add_runtime_dependency 'configatron', '~> 4.5.0' - s.add_runtime_dependency 'rest-client', '~> 1.8.0' - s.add_runtime_dependency 'wrong', '~> 0.7.1' - s.add_runtime_dependency 'rspec', '~> 3.4.0' - s.add_runtime_dependency 'varify', '~> 0.0.5' + s.add_runtime_dependency 'concurrent-ruby', '~> 1.0.1' s.add_development_dependency 'coveralls', '~> 0.8.13' s.add_development_dependency 'rake', '~> 11.1.2' s.add_development_dependency 'guard', '~> 2.13.0' diff --git a/lib/commands.rb b/lib/commands.rb index aba8106..5aaf593 100644 --- a/lib/commands.rb +++ b/lib/commands.rb @@ -10,20 +10,19 @@ program :version, Factor::VERSION program :description, 'Factor.io Server to run workflows' -command 'server' do |c| - c.syntax = 'factor server [options]' +command 'workflow' do |c| + c.syntax = 'factor workflow workflow_file' c.description = 'Start the Factor.io Server in the current local directory' - c.option '--log FILE', String, 'Log file path. Default is stdout.' - c.option '--credentials FILE', String, 'credentials.yml file path.' - c.option '--path FILE', String, 'Path to workflows' - c.when_called Factor::Commands::WorkflowCommand, :server + c.option '--settings FILE', String, 'factor.yml file path.' + c.when_called Factor::Commands::WorkflowCommand, :run end command 'run' do |c| c.syntax = 'factor run service_address params' c.description = 'Run a specific command.' - c.option '--credentials FILE', String, 'credentials.yml file path.' + c.option '--require FILE', String, 'file to require for loading method' c.when_called Factor::Commands::RunCommand, :run end -alias_command 's', 'server' \ No newline at end of file +alias_command 'w', 'workflow' +alias_command 'r', 'run' \ No newline at end of file diff --git a/lib/factor/commands/base.rb b/lib/factor/commands/base.rb index 31908fb..ea369fb 100644 --- a/lib/factor/commands/base.rb +++ b/lib/factor/commands/base.rb @@ -3,7 +3,7 @@ require 'configatron' require 'yaml' require 'fileutils' -require 'factor/logger/basic' +require 'factor/logger' module Factor module Commands @@ -11,47 +11,42 @@ module Commands class Command attr_accessor :logger - DEFAULT_FILENAME = { - credentials: File.expand_path('./credentials.yml') - } + DEFAULT_FILENAME = File.expand_path('./settings.yml') - def initialize - @logger = Factor::Log::BasicLogger.new - end - def load_config(options = {}) - load_config_data :credentials, options + def load_settings(options = {}) + relative_path = options.settings || DEFAULT_FILENAME + absolute_path = File.expand_path(relative_path) + content = File.read(absolute_path) + data = YAML.load(content) + configatron[:settings].configure_from_hash(data) end - def save_config(options={}) - credentials_relative_path = options[:credentials] || DEFAULT_FILENAME[:credentials] - credentials_absolute_path = File.expand_path(credentials_relative_path) - credentials = Hash[stringify(configatron.credentials.to_h).sort] - - File.write(credentials_absolute_path,YAML.dump(credentials)) + def settings + configatron.settings.to_hash end + private - def stringify(hash) - hash.inject({}) do |options, (key, value)| - options[key.to_s] = value.is_a?(Hash) ? stringify(value) : value - options + def try_json(value) + new_value = value + begin + new_value = JSON.parse(value, symbolize_names: true) + rescue JSON::ParserError end + new_value end - def load_config_data(config_type, options = {}) - relative_path = options[config_type] || DEFAULT_FILENAME[config_type] - absolute_path = File.expand_path(relative_path) - begin - data = YAML.load(File.read(absolute_path)) - rescue - data = {} + + def parse_data(args = []) + request_options = {} + args.each do |arg| + key,value = arg.split(/:/,2) + raise ArgumentError, "Option '#{arg}' is not a valid option" unless key && value + request_options[key.to_sym] = try_json(value) end - configatron[config_type].configure_from_hash(data) - rescue => ex - logger.error message:"Couldn't load #{config_type} from #{absolute_path}", exception:ex - exit + request_options end end end diff --git a/lib/factor/commands/run_command.rb b/lib/factor/commands/run_command.rb index 08b8d27..33c31f4 100644 --- a/lib/factor/commands/run_command.rb +++ b/lib/factor/commands/run_command.rb @@ -2,46 +2,27 @@ require 'json' require 'factor/commands/base' -require 'factor/workflow/runtime' +require 'factor/connector' +# require 'factor/workflow/runtime' module Factor module Commands class RunCommand < Factor::Commands::Command def run(args, options) - config_settings = {} - config_settings[:credentials] = options.credentials - load_config(config_settings) - - credential_settings = configatron.credentials.to_hash - runtime = Factor::Workflow::Runtime.new(credential_settings, logger: logger) - - begin - params = JSON.parse(args[1] || '{}') - rescue => ex - logger.error "'#{args[1]}' can't be parsed as JSON" - exit - end - - done = false - - begin - runtime.run(args[0],params) do |response_info| - data = response_info.is_a?(Array) ? response_info.map {|i| i.marshal_dump} : response_info.marshal_dump - JSON.pretty_generate(data).split("\n").each do |line| - logger.info line - end - done = true - end.on_fail do - done = true - end - rescue => ex - logger.error ex.message - done = true - end - - Factor::Common::Blocker.block_until_interrupt_or { done } - - logger.info 'Good bye!' + + address = args[0] + request_options = parse_data(args[1..-1]) + + require options.require if options.require + + connector_class = Factor::Connector.get(address) + + raise ArgumentError, "Connector '#{address}' not found" unless connector_class + + connector = connector_class.new(request_options) + response = connector.run + + puts response end end end diff --git a/lib/factor/commands/workflow_command.rb b/lib/factor/commands/workflow_command.rb index cd0330d..1099006 100644 --- a/lib/factor/commands/workflow_command.rb +++ b/lib/factor/commands/workflow_command.rb @@ -1,6 +1,5 @@ # encoding: UTF-8 -require 'factor/common/blocker' require 'factor/commands/base' require 'factor/workflow/runtime' @@ -8,80 +7,19 @@ module Factor module Commands # Workflow is a Command to start the factor runtime from the CLI class WorkflowCommand < Factor::Commands::Command - def initialize - @runtimes = [] - super - end - - def server(_args, options) - config_settings = {} - config_settings[:credentials] = options.credentials - workflow_filename = File.expand_path(options.path || '.') - @destination_stream = File.new(options.log, 'w+') if options.log - - load_config(config_settings) - load_all_workflows(workflow_filename) + def run(args, options) + workflow_filename = File.expand_path(args[0]) - if @runtimes.count > 0 - - logger.info 'Ctrl-c to exit' - Factor::Common::Blocker.block_until_interrupt_or do - @runtimes.all?{|r| r.stopped?} - end - - logger.info "Stopping..." - @runtimes.each {|r| r.unload if r.started? } - Factor::Common::Blocker.block_until sleep:0.5 do - @runtimes.all?{|r| r.stopped?} - end - end - logger.info 'Good bye!' + load_settings(options) if options.settings + load_workflow_from_file(workflow_filename) end private - def load_all_workflows(workflow_filename) - glob_ending = workflow_filename[-1] == '/' ? '' : '/' - glob = "#{workflow_filename}#{glob_ending}*.rb" - file_list = Dir.glob(glob) - if !file_list.all? { |file| File.file?(file) } - logger.error "#{workflow_filename} is neither a file or directory" - elsif file_list.count == 0 - logger.error 'No workflows in this directory to run' - else - file_list.each { |filename| load_workflow(File.expand_path(filename)) } - end - end - - def load_workflow(workflow_filename) - logger.info "Loading workflow from #{workflow_filename}" - begin - workflow_definition = File.read(workflow_filename) - rescue => ex - logger.error "Couldn't read file #{workflow_filename}", exception:ex - return - end - - load_workflow_from_definition(workflow_definition, File.basename(workflow_filename)) - end - - def load_workflow_from_definition(workflow_definition, filename) - logger.info "Setting up workflow processor" - begin - credential_settings = configatron.credentials.to_hash - runtime = Factor::Workflow::Runtime.new(credential_settings, logger: logger, workflow_filename: filename) - @runtimes << runtime - rescue => ex - message = "Couldn't setup workflow process" - logger.error message:message, exception:ex - end - - begin - logger.info "Starting workflow" - runtime.load(workflow_definition) - rescue => ex - logger.error message: "Couldn't start workflow", exception: ex - end + def load_workflow_from_file(workflow_filename) + workflow_definition = File.read(workflow_filename) + runtime = Factor::Workflow::Runtime.new(settings) + runtime.load workflow_definition, workflow_filename end end end diff --git a/lib/factor/common/blocker.rb b/lib/factor/common/blocker.rb deleted file mode 100644 index eb4b863..0000000 --- a/lib/factor/common/blocker.rb +++ /dev/null @@ -1,44 +0,0 @@ -module Factor - module Common - module Blocker - - def self.block_until(options = {}, &block) - pause = options[:sleep] || 0.1 - - begin - continue = block.yield - sleep pause - end while !continue - end - - def self.block_until_interrupt_or(options = {}, &block) - pause = options[:sleep] || 0.1 - interrupted = false - - Thread.new do - block_until_interrupt - interrupted = true - end - - begin - begin - until_met = block.yield - sleep pause - rescue Interrupt - interrupted = true - end - stop_looping = until_met || interrupted - end until stop_looping - end - - def self.block_until_interrupt - begin - begin - sleep 0.1 - end while true - rescue Interrupt - end - end - end - end -end \ No newline at end of file diff --git a/lib/factor/common/deep_struct.rb b/lib/factor/common/deep_struct.rb deleted file mode 100644 index 6da0860..0000000 --- a/lib/factor/common/deep_struct.rb +++ /dev/null @@ -1,56 +0,0 @@ -require 'ostruct' - -module Factor - module Common - class DeepStruct < OpenStruct - def initialize(hash=nil) - @table = {} - @hash_table = {} - - if hash - hash.each do |k,v| - @table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v) - @hash_table[k.to_sym] = v - - new_ostruct_member(k) - end - end - end - - def to_h - @hash_table - end - - def to_s - @hast_table.to_s - end - - def inspect - @hash_table.inspect - end - - def [](idx) - hash = marshal_dump - hash[idx.to_sym] - end - end - - def self.flat_hash(h,f=[],g={}) - return g.update({ f=>h }) unless h.is_a? Hash - h.each { |k,r| flat_hash(r,f+[k],g) } - g - end - - def self.simple_object_convert(item) - if item.is_a?(Hash) - Factor::Common::DeepStruct.new(item) - elsif item.is_a?(Array) - item.map do |i| - simple_object_convert(i) - end - else - item - end - end - end -end \ No newline at end of file diff --git a/lib/factor/connector.rb b/lib/factor/connector.rb new file mode 100644 index 0000000..acf947f --- /dev/null +++ b/lib/factor/connector.rb @@ -0,0 +1,40 @@ +require 'observer' + +module Factor + class Connector + include Observable + def self.register(connector) + if connector.superclass != Factor::Connector + raise ArgumentError, "Connector must be a Factor::Connector" + end + + @@paths ||= {} + @@paths[underscore(connector.name)] = connector + end + + def self.get(path) + @@paths ||= {} + @@paths[path] + end + + + def run + end + + def trigger(data) + changed + notify_observers(:trigger, data) + end + + private + + def self.underscore(string) + word = string.dup + word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2') + word.gsub!(/([a-z\d])([A-Z])/,'\1_\2') + word.tr!("-", "_") + word.downcase! + word + end + end +end diff --git a/lib/factor/connector/definition.rb b/lib/factor/connector/definition.rb deleted file mode 100644 index 20cdb6f..0000000 --- a/lib/factor/connector/definition.rb +++ /dev/null @@ -1,124 +0,0 @@ -require 'observer' -require 'varify' - -require 'factor/connector/error' - -module Factor - module Connector - class Definition - include Observable - include Varify - - def initialize - Varify::Base.callback do |failure| - fail failure[:message] - end - end - - def self.id(id) - raise ArgumentError, "ID must be a sym" unless id.is_a?(Symbol) - instance_variable_set('@id',id) - define_method :id do - id - end - end - - def self.resource(resource,&block) - resources = instance_variable_get('@resources') || [] - resources.push resource - instance_variable_set('@resources',resources) - - block.call - - resources.pop - instance_variable_set('@resources',resources) - remove_instance_variable('@resources') if resources.count == 0 - end - - def self.action(action,&block) - resources = instance_variable_get('@resources') || [] - address = resources + [action] - actions = instance_variable_get('@actions') || {} - - actions[address] = block - instance_variable_set('@actions', actions) - end - - def self.listener(listener,&block) - resources = instance_variable_get('@resources') || [] - address = resources + [listener] - start_address = resources + [listener] + [:start] - stop_address = resources + [listener] + [:stop] - - instance_variable_set('@listener', listener) - - block.call - - listeners = instance_variable_get('@listeners') || {} - raise ArgumentError, "Start block must be defined in listener '#{address.join('::')}'" unless listeners[start_address] - raise ArgumentError, "Stop block must be defined in listener '#{address.join('::')}'" unless listeners[stop_address] - - remove_instance_variable('@listener') - end - - def self.start(&block) - listener = instance_variable_get('@listener') - raise ArgumentError, 'Start block must be defined within a Listener' unless listener - resources = instance_variable_get('@resources') || [] - address = resources + [listener] + [:start] - - listeners = instance_variable_get('@listeners') || {} - listeners[address] = block - instance_variable_set('@listeners', listeners) - end - - def self.stop(&block) - listener = instance_variable_get('@listener') - raise ArgumentError, 'Stop block must be defined within a Listener' unless listener - resources = instance_variable_get('@resources') || [] - address = resources + [listener] + [:stop] - - listeners = instance_variable_get('@listeners') || {} - listeners[address] = block - instance_variable_set('@listeners', listeners) - end - - def trigger(data) - changed - notify_observers type:'trigger', data:data - end - - def respond(data) - changed - notify_observers type:'response', data: data - end - - def info(message) - log 'info', message - end - - def error(message) - log 'error', message - end - - def warn(message) - log 'warn', message - end - - def debug(message) - log 'debug', message - end - - def fail(message,params={}) - changed - notify_observers type:'fail', message: message - raise Factor::Connector::Error, exception:params[:exception], message:message if !params[:throw] - end - - def log(status, message) - changed - notify_observers type: 'log', status: status, message: message - end - end - end -end \ No newline at end of file diff --git a/lib/factor/connector/error.rb b/lib/factor/connector/error.rb deleted file mode 100644 index e89cab5..0000000 --- a/lib/factor/connector/error.rb +++ /dev/null @@ -1,14 +0,0 @@ -# encoding: UTF-8 - -module Factor - module Connector - class Error < StandardError - attr_accessor :state, :exception - - def initialize(params = {}) - @exception = params[:exception] - super(params[:message] || '') - end - end - end -end \ No newline at end of file diff --git a/lib/factor/connector/registry.rb b/lib/factor/connector/registry.rb deleted file mode 100644 index 715f05a..0000000 --- a/lib/factor/connector/registry.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Factor - module Connector - module Registry - - def self.get(id) - begin - require "factor-connector-#{id}" - rescue LoadError - end - get_class = self.class.constants.find do |class_name| - begin - class_obj = self.class.const_get(class_name) - class_obj.superclass == Factor::Connector::Definition && class_obj.new.id == id - rescue - end - end - raise "No definition found with id #{id}" unless get_class - self.class.const_get(get_class) - end - end - end -end \ No newline at end of file diff --git a/lib/factor/connector/runtime.rb b/lib/factor/connector/runtime.rb deleted file mode 100644 index b2485b6..0000000 --- a/lib/factor/connector/runtime.rb +++ /dev/null @@ -1,97 +0,0 @@ -require 'observer' - -module Factor - module Connector - class Runtime - include Observable - attr_reader :logs, :state - - def initialize(connector) - @connector = connector.new - @connector.add_observer(self, :log) - @logs = [] - @state = :stopped - end - - def started? - @state == :started - end - - def starting? - @state == :starting - end - - def stopped? - @state == :stopped - end - - def stopping? - @state == :stopping - end - - def callback=(block) - @callback = block if block - end - - def callback(&block) - @callback = block if block - end - - def log(params) - @logs << params - changed - notify_observers params - @callback.call(params) if @callback - end - - def run(address, options={}) - raise ArgumentError, "Address must be an Array" unless address.is_a?(Array) - raise ArgumentError, "Address must be an Array of Symbols" unless address.all?{|a| a.is_a?(Symbol)} - raise ArgumentError, "Address must not be empty" unless address.length > 0 - @address = address - actions = @connector.class.instance_variable_get('@actions') - action = actions[address] - raise ArgumentError, "Action #{address} not found" unless action - Thread.new do - begin - @connector.instance_exec(options,&action) - rescue => ex - log type:'fail', message: ex.message - end - end - end - - def start_listener(address, options={}) - @address = address - listeners = @connector.class.instance_variable_get('@listeners') - listener = listeners[address + [:start]] - raise ArgumentError, "Listener #{address} not found" unless listener - @start_listener_thread = Thread.new do - @state = :starting - begin - @connector.instance_exec(options, &listener) - @state = :started - rescue => ex - @state = :stopped - log type:'fail', message: ex.message - end - end - end - - def stop_listener - listeners = @connector.class.instance_variable_get('@listeners') - listener = listeners[@address + [:stop]] - raise ArgumentError, "Listener #{address} not found" unless listener - - Thread.new do - @state = :stopping - begin - @connector.instance_eval(&listener) - ensure - @state = :stopped - end - end - end - end - end -end \ No newline at end of file diff --git a/lib/factor/logger.rb b/lib/factor/logger.rb new file mode 100644 index 0000000..0c4d84d --- /dev/null +++ b/lib/factor/logger.rb @@ -0,0 +1,69 @@ +require 'rainbow' + +module Factor + class Logger + + attr_accessor :destination_stream + + def log(section, options={}) + options = { message: options } if options.is_a?(String) + tag = tag(options) + message = options['message'] || options[:message] + section_text = format_section(section) + write "[ #{section_text} ] [#{time}]#{tag} #{message}" if message + exception options[:exception] if options[:exception] + end + + def info(options = {}) + log :info, options + end + + def warn(options = {}) + log :warn, options + end + + def error(options = {}) + log :error, options + end + + def success(options = {}) + log :success, options + end + + private + + def exception(exception) + error message: " #{exception.message}" + exception.backtrace.each do |line| + error message: " #{line}" + end + end + + def format_section(section) + formated_section = section.to_s.upcase.center(10) + case section.to_sym + when :error then Rainbow(formated_section).red + when :info then Rainbow(formated_section).white.bright + when :warn then Rainbow(formated_section).yellow + when :success then Rainbow(formated_section).green + else formated_section + end + end + + def tag(options) + primary = options['service_id'] || options['instance_id'] + secondary = ":#{options['instane_id']}" if options['service_id'] && options['instance_id'] + primary ? "[#{primary}#{secondary || ''}]" : '' + end + + def write(message) + stream = @destination_stream || $stdout + stream.puts(message) + stream.flush + end + + def time + Time.now.localtime.strftime('%m/%d/%y %T.%L') + end + end +end \ No newline at end of file diff --git a/lib/factor/logger/basic.rb b/lib/factor/logger/basic.rb deleted file mode 100644 index dc666a4..0000000 --- a/lib/factor/logger/basic.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'rainbow' -require 'factor/logger/logger' - -module Factor - module Log - class BasicLogger < Factor::Log::Logger - - attr_accessor :destination_stream - - def log(section, options={}) - options = { message: options } if options.is_a?(String) - tag = tag(options) - message = options['message'] || options[:message] - section_text = format_section(section) - write "[ #{section_text} ] [#{time}]#{tag} #{message}" if message - exception options[:exception] if options[:exception] - end - - def info(options = {}) - log :info, options - end - - def warn(options = {}) - log :warn, options - end - - def error(options = {}) - log :error, options - end - - def success(options = {}) - log :success, options - end - - private - - def exception(exception) - error message: " #{exception.message}" - exception.backtrace.each do |line| - error message: " #{line}" - end - end - - def format_section(section) - formated_section = section.to_s.upcase.center(10) - case section.to_sym - when :error then Rainbow(formated_section).red - when :info then Rainbow(formated_section).white.bright - when :warn then Rainbow(formated_section).yellow - when :success then Rainbow(formated_section).green - else formated_section - end - end - - def tag(options) - primary = options['service_id'] || options['instance_id'] - secondary = ":#{options['instane_id']}" if options['service_id'] && options['instance_id'] - primary ? "[#{primary}#{secondary || ''}]" : '' - end - - def write(message) - stream = @destination_stream || $stdout - stream.puts(message) - stream.flush - end - - end - end -end \ No newline at end of file diff --git a/lib/factor/logger/logger.rb b/lib/factor/logger/logger.rb deleted file mode 100644 index 5cb1220..0000000 --- a/lib/factor/logger/logger.rb +++ /dev/null @@ -1,28 +0,0 @@ - -module Factor - module Log - - class Logger - - def log - raise NotImplemented - end - - def info - raise NotImplemented - end - - def warn - raise NotImplemented - end - - def error - raise NotImplemented - end - - def time - Time.now.localtime.strftime('%m/%d/%y %T.%L') - end - end - end -end \ No newline at end of file diff --git a/lib/factor/logger/test.rb b/lib/factor/logger/test.rb deleted file mode 100644 index b19f9dd..0000000 --- a/lib/factor/logger/test.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'factor/logger/logger' - -module Factor - module Log - class TestLogger < Factor::Log::Logger - - attr_reader :history - - def initialize - @history = [] - end - - def log(section, message='') - history << {status: section, message:message} - end - - def info(message = '') - log :info, message - end - - def warn(message = '') - log :warn, message - end - - def error(message = '') - log :error, message - end - - def success(message = '') - log :success, message - end - - def clear - @history=[] - end - end - end -end \ No newline at end of file diff --git a/lib/factor/workflow/connector_future.rb b/lib/factor/workflow/connector_future.rb new file mode 100644 index 0000000..882e7f6 --- /dev/null +++ b/lib/factor/workflow/connector_future.rb @@ -0,0 +1,27 @@ +require 'factor/workflow/future' + +module Factor + module Workflow + class ConnectorFuture < Future + def initialize(action) + @subscribers = {} + @action = action + @action.add_observer(self, :trigger) + + super() do + @action.run + end + end + + def trigger(type, data) + @subscribers[type] ||= [] + @subscribers[type].each {|subscriber| subscriber.call(data)} + end + + def on(type, &block) + @subscribers[type] ||= [] + @subscribers[type] << block + end + end + end +end \ No newline at end of file diff --git a/lib/factor/workflow/definition.rb b/lib/factor/workflow/definition.rb deleted file mode 100644 index 9d2b8c1..0000000 --- a/lib/factor/workflow/definition.rb +++ /dev/null @@ -1,174 +0,0 @@ -# encoding: UTF-8 - -require 'factor/commands/base' -require 'factor/common/deep_struct' -require 'factor/common/blocker' -require 'factor/workflow/service_address' -require 'factor/workflow/exec_handler' -require 'factor/connector/runtime' -require 'factor/connector/registry' -require 'factor/connector/definition' - -module Factor - module Workflow - class Definition - attr_reader :state - - def initialize(credentials, options={}) - @logger = options[:logger] if options[:logger] - @credentials = credentials - @workflow_filename = options[:workflow_filename] - @unload = false - @connector_runtimes = [] - end - - def stop - @state = :stopping - @unload = true - end - - def state - empty = @connector_runtimes.count == 0 - all_started = @connector_runtimes.all? {|r| r.started? } - all_stopped = @connector_runtimes.all? {|r| r.stopped? } - any_stopping = @connector_runtimes.any? {|r| r.stopping? } - any_starting = @connector_runtimes.any? {|r| r.starting? } - - if empty || all_stopped - :stopped - elsif all_started - :started - elsif any_stopping - :stopping - elsif any_starting - :starting - else - :stopped - end - end - - def started? - state == :started - end - - def starting? - state == :starting - end - - def stopped? - state == :stopped - end - - def stopping? - state == :stopping - end - - def listen(service_ref, params = {}, &block) - address, connector_runtime, exec, params_and_creds = initialize_connector_runtime(service_ref,params) - line = caller.first.split(":")[1] - @context = @workflow_filename ? "#{service_ref}(#{@workflow_filename}:#{line})" : "#{service_ref}" - @connector_runtimes << connector_runtime - - done = false - - connector_runtime.callback do |response| - message = response[:message] - type = response[:type] - - case type - when 'trigger' - success "Triggered" - block.call(Factor::Common.simple_object_convert(response[:payload])) if block - when 'log' - log_callback(message,response[:status]) - when 'fail' - message = response[:message] || 'unkonwn error' - error "Failed: #{message}" - exec.fail_block.call(message) if exec.fail_block - done = true - end - end - - success "Starting" - connector_runtime.start_listener(address.path, params) - - Thread.new do - Factor::Common::Blocker.block_until { done || @unload } - - success "Stopping" - connector_runtime.stop_listener - success "Stopped" - @connector_runtimes.delete(connector_runtimes) - end - - exec - end - - def run(service_ref, params = {}, &block) - address, connector_runtime, exec, params_and_creds = initialize_connector_runtime(service_ref,params) - line = caller.first.split(":")[1] - @context = @workflow_filename ? "#{service_ref}(#{@workflow_filename}:#{line})" : "#{service_ref}" - - connector_runtime.callback do |response| - message = response[:message] - type = response[:type] - - case type - when 'log' - log_callback(message,response[:status]) - when 'fail' - error_message = response[:message] || "unknown error" - error "Failed: #{error_message}" - exec.fail_block.call(message) if exec.fail_block - when 'response' - success "Completed" - payload = response[:payload] || {} - block.call(Factor::Common.simple_object_convert(payload)) if block - end - end - - success "Starting" - listener_instance = connector_runtime.run(address.path, params_and_creds) - exec - end - - def success(message) - @logger.success @context ? "[#{@context}] #{message}" : message - end - - def info(message) - @logger.info @context ? "[#{@context}] #{message}" : message - end - - def warn(message) - @logger.warn @context ? "[#{@context}] #{message}" : message - end - - def error(message) - @logger.error @context ? "[#{@context}] #{message}" : message - end - - private - - def initialize_connector_runtime(service_ref, params={}) - address = Factor::Workflow::ServiceAddress.new(service_ref) - service_credentials = @credentials[address.service.to_sym] || {} - exec = Factor::Workflow::ExecHandler.new(service_ref, params) - connector_class = Factor::Connector::Registry.get(address.service) - connector_runtime = Factor::Connector::Runtime.new(connector_class) - params_and_creds = Factor::Common::DeepStruct.new(params.merge(service_credentials)).to_h - - [address, connector_runtime, exec, params_and_creds] - end - - def log_callback(message,status) - case status - when 'info' then info message - when 'warn' then warn message - when 'error' then error message - when 'debug' then error message - end - end - end - end -end diff --git a/lib/factor/workflow/dsl.rb b/lib/factor/workflow/dsl.rb new file mode 100644 index 0000000..a9bb582 --- /dev/null +++ b/lib/factor/workflow/dsl.rb @@ -0,0 +1,34 @@ +require 'factor/workflow/connector_future' + +module Factor + module Workflow + class DSL + def initialize(options={}) + @options=options + end + + def run(address,options={}, &block) + + connector_class = Factor::Connector.get(address) + connector = connector_class.new(options) + + Factor::Workflow::ConnectorFuture.new(connector) + end + + def all(*events, &block) + Future.all(*events, &block) + end + + def any(number=1, *events, &block) + Future.any(number, events, &block) + end + + def on(type, *actions, &block) + raise ArgumentError, "All actions must be an ConnectorFuture" unless actions.all? {|a| a.is_a?(ConnectorFuture) } + actions.each {|a| a.on(type, &block) } + end + + end + + end +end \ No newline at end of file diff --git a/lib/factor/workflow/exec_handler.rb b/lib/factor/workflow/exec_handler.rb deleted file mode 100644 index 4bb7c40..0000000 --- a/lib/factor/workflow/exec_handler.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Factor - module Workflow - class ExecHandler - attr_accessor :params, :service, :fail_block - - def initialize(service = nil, params = {}) - @service = service - @params = params - end - - def on_fail(&block) - @fail_block = block - end - end - end -end \ No newline at end of file diff --git a/lib/factor/workflow/future.rb b/lib/factor/workflow/future.rb new file mode 100644 index 0000000..13f53ec --- /dev/null +++ b/lib/factor/workflow/future.rb @@ -0,0 +1,74 @@ +require 'concurrent' + +module Factor + module Workflow + class Future + extend Forwardable + + def initialize(promise=nil, &block) + raise ArgumentError, "promise or block required" unless promise || block + raise ArgumentError, "promise and block can't both be provided" if promise && block + raise ArgumentError, "promise must be a Concurrent::Promise" if promise && !promise.is_a?(Concurrent::Promise) + + @promise = promise || Concurrent::Promise.new(&block) + end + + def_delegator :@promise, :state, :state + def_delegator :@promise, :pending?, :pending? + def_delegator :@promise, :rejected?, :rejected? + def_delegator :@promise, :fulfilled?, :fulfilled? + def_delegator :@promise, :unscheduled?, :unscheduled? + def_delegator :@promise, :reason, :reason + def_delegator :@promise, :value, :value + def_delegator :@promise, :fail, :fail + + def completed? + @promise.fulfilled? || @promise.rejected? + end + + def then(&block) + Future.new(@promise.then(&block)) + end + + def execute + Future.new(@promise.execute) + end + + def rescue(&block) + Future.new(@promise.rescue(&block)) + end + + def depth + count = 0 + begin + parent=@promise.parent + count +=1 if parent + end while parent + count + end + + def wait + @promise.execute if @promise.unscheduled? + @promise.wait + end + + def self.all(*handlers, &block) + block ||= lambda {|v| true} + Future.new do + handlers.each {|handler| handler.execute if handler.unscheduled?} + completed = handlers.map do |handler| + handler.wait + handler + end + completed.all? do |future| + future.fulfilled? && block.call(future.value) + end + end + end + + def self.any(number=1, *handlers, &block) + + end + end + end +end \ No newline at end of file diff --git a/lib/factor/workflow/runtime.rb b/lib/factor/workflow/runtime.rb index 462fa34..8ac6ce3 100644 --- a/lib/factor/workflow/runtime.rb +++ b/lib/factor/workflow/runtime.rb @@ -1,26 +1,17 @@ # encoding: UTF-8 -require 'forwardable' - -require 'factor/workflow/definition' +require 'factor/workflow/dsl' module Factor module Workflow class Runtime extend Forwardable - def initialize(credentials, options={}) - @definition = Factor::Workflow::Definition.new(credentials, options) + def initialize(options={}) + @options = options + @dsl = DSL.new(@options) end - def_delegator :@definition, :instance_eval, :load - def_delegator :@definition, :run, :run - def_delegator :@definition, :stop, :unload - def_delegator :@definition, :state, :state - def_delegator :@definition, :started?, :started? - def_delegator :@definition, :starting?, :starting? - def_delegator :@definition, :stopped?, :stopped? - def_delegator :@definition, :stopping?, :stopping? - + def_delegator :@dsl, :instance_eval, :load end end end diff --git a/lib/factor/workflow/service_address.rb b/lib/factor/workflow/service_address.rb deleted file mode 100644 index c57c952..0000000 --- a/lib/factor/workflow/service_address.rb +++ /dev/null @@ -1,50 +0,0 @@ -module Factor - module Workflow - class ServiceAddress < Array - def initialize(service_ref) - if service_ref.is_a?(String) - service_map = service_ref.split('::').map {|i| i.to_sym} - raise ArgumentError, 'Address must not be empty' if service_ref.empty? - raise ArgumentError, 'Address must contain at least the service name and action' unless service_map.count > 1 - raise ArgumentError, 'Address must not contain empty references' unless service_map.all?{|i| !i.empty?} - super service_map - elsif service_ref.is_a?(ServiceAddress) || service_ref.is_a?(Array) - raise ArgumentError, 'All elements in array must be a string' unless service_ref.all?{|i| i.is_a?(String) || i.is_a?(Symbol)} - super service_ref - else - raise ArgumentError, 'Address must be a String, Array, or ServiceAddress' - end - end - - def workflow? - self.service == :workflow - end - - def service - self.first - end - - def namespace - raise ArgumentError, 'Address must contain at least two parts' unless self.count >= 2 - self[0..-2] - end - - def id - self.last - end - - def to_s - self.join('::') - end - - def resource - raise "No resource path defined, address must contain at least three parts" unless self.length >= 3 - self[1..-2] - end - - def path - self[1..-1] - end - end - end -end diff --git a/spec/common/deep_struct_spec.rb b/spec/common/deep_struct_spec.rb deleted file mode 100644 index 947a91b..0000000 --- a/spec/common/deep_struct_spec.rb +++ /dev/null @@ -1,51 +0,0 @@ -# encoding: UTF-8 - -require 'spec_helper' - -require 'factor/common/deep_struct' - -describe Factor::Common::DeepStruct do - it 'handles depth 1 Hash' do - source = { - a:'b' - } - - result = Factor::Common.simple_object_convert source - - expect(result).to respond_to(:a) - expect(result.a).to eq('b') - end - - it 'handles depth n Hash' do - source = { - a:{ - b: 'c' - } - } - - result = Factor::Common.simple_object_convert source - - expect(result).to respond_to(:a) - expect(result).to respond_to(:to_h) - expect(result.a).to respond_to(:b, :to_h) - expect(result.a.to_h).to eq(b:'c') - expect(result.a.b).to eq('c') - - end - - it 'handles array of Hash' do - source = [ - {a:'b'}, - {c:'d'} - ] - - result = Factor::Common.simple_object_convert source - - expect(result).to be_a(Array) - expect(result).to all(be_a(Factor::Common::DeepStruct)) - expect(result[0]).to respond_to(:a) - expect(result[0].a).to eq('b') - expect(result[1]).to respond_to(:c) - expect(result[1].c).to eq('d') - end -end From 0d0da6c6e0cb762d152b9fb9bf740f06474a77ad Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Sat, 16 Apr 2016 18:35:15 -0700 Subject: [PATCH 04/16] Tiny bit of cleanup --- lib/factor/commands/base.rb | 2 +- lib/factor/commands/run_command.rb | 2 +- lib/factor/commands/workflow_command.rb | 5 ----- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/factor/commands/base.rb b/lib/factor/commands/base.rb index ea369fb..d1d0146 100644 --- a/lib/factor/commands/base.rb +++ b/lib/factor/commands/base.rb @@ -39,7 +39,7 @@ def try_json(value) end - def parse_data(args = []) + def params(args = []) request_options = {} args.each do |arg| key,value = arg.split(/:/,2) diff --git a/lib/factor/commands/run_command.rb b/lib/factor/commands/run_command.rb index 33c31f4..5b53093 100644 --- a/lib/factor/commands/run_command.rb +++ b/lib/factor/commands/run_command.rb @@ -11,7 +11,7 @@ class RunCommand < Factor::Commands::Command def run(args, options) address = args[0] - request_options = parse_data(args[1..-1]) + request_options = params(args[1..-1]) require options.require if options.require diff --git a/lib/factor/commands/workflow_command.rb b/lib/factor/commands/workflow_command.rb index 1099006..0e7ce5b 100644 --- a/lib/factor/commands/workflow_command.rb +++ b/lib/factor/commands/workflow_command.rb @@ -11,12 +11,7 @@ def run(args, options) workflow_filename = File.expand_path(args[0]) load_settings(options) if options.settings - load_workflow_from_file(workflow_filename) - end - - private - def load_workflow_from_file(workflow_filename) workflow_definition = File.read(workflow_filename) runtime = Factor::Workflow::Runtime.new(settings) runtime.load workflow_definition, workflow_filename From f81e3c2d660e7a7ecabc9ecc779bf18e3dd07068 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Sun, 17 Apr 2016 01:07:18 -0700 Subject: [PATCH 05/16] closes #63 --- lib/factor/workflow/dsl.rb | 4 ++-- lib/factor/workflow/future.rb | 33 +++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/lib/factor/workflow/dsl.rb b/lib/factor/workflow/dsl.rb index a9bb582..556ac6a 100644 --- a/lib/factor/workflow/dsl.rb +++ b/lib/factor/workflow/dsl.rb @@ -19,8 +19,8 @@ def all(*events, &block) Future.all(*events, &block) end - def any(number=1, *events, &block) - Future.any(number, events, &block) + def any(*events, &block) + Future.any(*events, &block) end def on(type, *actions, &block) diff --git a/lib/factor/workflow/future.rb b/lib/factor/workflow/future.rb index 13f53ec..9f4bfdf 100644 --- a/lib/factor/workflow/future.rb +++ b/lib/factor/workflow/future.rb @@ -38,15 +38,6 @@ def rescue(&block) Future.new(@promise.rescue(&block)) end - def depth - count = 0 - begin - parent=@promise.parent - count +=1 if parent - end while parent - count - end - def wait @promise.execute if @promise.unscheduled? @promise.wait @@ -60,14 +51,28 @@ def self.all(*handlers, &block) handler.wait handler end - completed.all? do |future| - future.fulfilled? && block.call(future.value) - end + worked = completed.all? { |handler| handler.fulfilled? && block.call(handler.value) } + + raise StandardError, "At least one event failed" unless worked + + worked end end - def self.any(number=1, *handlers, &block) - + def self.any(*handlers, &block) + block ||= lambda {|v| true} + Future.new do + handlers.each {|handler| handler.execute if handler.unscheduled?} + completed = handlers.map do |handler| + handler.wait + handler + end + worked = completed.any? { |handler| handler.fulfilled? && block.call(handler.value) } + + raise StandardError, "There were no successful events" unless worked + + worked + end end end end From 180966821bb54ced892ddff2f102dc892a1cc47c Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Sun, 17 Apr 2016 09:32:28 -0700 Subject: [PATCH 06/16] Test reset --- spec/commands/base_spec.rb | 68 +------------------- spec/commands/run_spec.rb | 13 ++++ spec/commands/workflow_spec.rb | 2 +- spec/connector/definition_spec.rb | 78 ----------------------- spec/connector/registry_spec.rb | 28 --------- spec/connector/runtime_spec.rb | 83 ------------------------- spec/connector_spec.rb | 11 ++++ spec/logger_spec.rb | 13 ++++ spec/spec_helper.rb | 12 ---- spec/workflow/runtime_spec.rb | 89 --------------------------- spec/workflow/service_address_spec.rb | 71 --------------------- spec/workflow_spec.rb | 13 ++++ 12 files changed, 52 insertions(+), 429 deletions(-) create mode 100644 spec/commands/run_spec.rb delete mode 100644 spec/connector/definition_spec.rb delete mode 100644 spec/connector/registry_spec.rb delete mode 100644 spec/connector/runtime_spec.rb create mode 100644 spec/connector_spec.rb create mode 100644 spec/logger_spec.rb delete mode 100644 spec/workflow/runtime_spec.rb delete mode 100644 spec/workflow/service_address_spec.rb create mode 100644 spec/workflow_spec.rb diff --git a/spec/commands/base_spec.rb b/spec/commands/base_spec.rb index 910decf..55272b3 100644 --- a/spec/commands/base_spec.rb +++ b/spec/commands/base_spec.rb @@ -11,72 +11,6 @@ @command = Factor::Commands::Command.new end - output_methods = %w(info warn error success) - - output_methods.each do |method_name| - describe ".#{method_name}" do - it "logs #{method_name}" do - - test_string = 'Hello World' - output = capture_stdout do - @command.logger.method(method_name.to_sym).call message: test_string - end - - expect(output).to include(test_string) - expect(output).to include(method_name.upcase) - end - end - end - - describe '.exception' do - it 'logs exception' do - - test_string = 'Hello World' - exception_string = 'Something be busted' - output = capture_stdout do - begin - raise ArgumentError, exception_string - rescue => ex - @command.logger.error message: test_string, exception:ex - end - end - - expect(output).to include(test_string) - expect(output).to include(exception_string) - expect(output).to include('ERROR') - - end - end - - describe '.load_config' do - it 'can load credentials' do - credentials_file = Tempfile.new('credentials') - - credentials_content = { - 'github' => { - 'api_key' => 'fake_github_key' - }, - 'heroku' => { - 'api_key' => 'fake_heroku_key' - } - } - - credentials_file.write(YAML.dump(credentials_content)) - credentials_file.rewind - options = Commander::Command::Options.new - options.credentials = credentials_file.path - config_settings = { - credentials: options.credentials, - } - - output = capture_stdout do - @command.load_config config_settings - end - - expect(configatron.credentials.github.api_key).to eq('fake_github_key') - expect(configatron.credentials.heroku.api_key).to eq('fake_heroku_key') - - credentials_file.close - end + it 'can' do end end diff --git a/spec/commands/run_spec.rb b/spec/commands/run_spec.rb new file mode 100644 index 0000000..f42d350 --- /dev/null +++ b/spec/commands/run_spec.rb @@ -0,0 +1,13 @@ +# encoding: UTF-8 + +require 'spec_helper' + +require 'factor/commands/run_command' + +describe Factor::Commands::RunCommand do + describe 'run' do + it 'can run a basic workflow' do + + end + end +end diff --git a/spec/commands/workflow_spec.rb b/spec/commands/workflow_spec.rb index 7597607..28902cb 100644 --- a/spec/commands/workflow_spec.rb +++ b/spec/commands/workflow_spec.rb @@ -5,7 +5,7 @@ require 'factor/commands/workflow_command' describe Factor::Commands::WorkflowCommand do - describe '.server' do + describe 'workflow' do it 'can run a basic workflow' do end diff --git a/spec/connector/definition_spec.rb b/spec/connector/definition_spec.rb deleted file mode 100644 index 9e861bb..0000000 --- a/spec/connector/definition_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -# encoding: UTF-8 - -require 'spec_helper' - -require 'factor/connector/definition' - -describe Factor::Connector::Definition do - before do - class MyDef < Factor::Connector::Definition - id :my_def - action :action_1 do - end - listener :listener_1 do - start do |params| - end - stop do - end - end - resource :nest_1 do - action :action_nested_1 do - end - listener :listener_nested_1 do - start do |params| - end - stop do |params| - end - end - resource :nest_2 do - action :action_nested_2 do - end - listener :listener_nested_2 do - start do |params| - end - stop do |params| - end - end - end - end - end - @definition = MyDef.new - end - - it 'can set the ID' do - expect(@definition).to respond_to(:id) - expect(@definition.id).to eq(:my_def) - end - - it 'can define an action' do - actions = @definition.class.instance_variable_get('@actions') - expect_proc_in(actions,[:action_1]) - end - - it 'can define a listener' do - listeners = @definition.class.instance_variable_get('@listeners') - expect_proc_in(listeners,[:listener_1,:start]) - expect_proc_in(listeners,[:listener_1,:stop]) - end - - it 'can created nested actions and listeners' do - actions = @definition.class.instance_variable_get('@actions') - listeners = @definition.class.instance_variable_get('@listeners') - - expect_proc_in(actions,[:nest_1,:action_nested_1]) - expect_proc_in(actions,[:nest_1,:nest_2,:action_nested_2]) - expect_proc_in(listeners, [:nest_1, :listener_nested_1, :start]) - expect_proc_in(listeners, [:nest_1, :listener_nested_1, :stop]) - expect_proc_in(listeners, [:nest_1, :nest_2, :listener_nested_2, :start]) - expect_proc_in(listeners, [:nest_1, :nest_2, :listener_nested_2, :stop]) - end - - def expect_proc_in(method,key) - expect(method).to_not be(nil) - expect(method).to be_a(Hash) - expect(method.keys).to include(key) - expect(method[key]).to_not be(nil) - expect(method[key]).to be_a(Proc) - end -end diff --git a/spec/connector/registry_spec.rb b/spec/connector/registry_spec.rb deleted file mode 100644 index 2465f2e..0000000 --- a/spec/connector/registry_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -# encoding: UTF-8 - -require 'spec_helper' - -require 'factor/connector/definition' -require 'factor/connector/registry' - -describe Factor::Connector::Registry do - before do - class MyDef < Factor::Connector::Definition - id :my_def - end - - end - - it 'can load definition by ID' do - definition = Factor::Connector::Registry.get(:my_def) - expect(definition).to eq(MyDef) - expect(definition.superclass).to eq(Factor::Connector::Definition) - end - - it 'fails on bad ID' do - expect { - Factor::Connector::Registry.get(:foo) - }.to raise_error - end - -end diff --git a/spec/connector/runtime_spec.rb b/spec/connector/runtime_spec.rb deleted file mode 100644 index af15090..0000000 --- a/spec/connector/runtime_spec.rb +++ /dev/null @@ -1,83 +0,0 @@ -# encoding: UTF-8 - -require 'spec_helper' - -require 'factor/connector/definition' -require 'factor/connector/runtime' -require 'factor/connector/test' - -describe Factor::Connector::Runtime do - include Factor::Connector::Test - - before do - class MyDef < Factor::Connector::Definition - id :my_def - def initialize - @some_var='some_var' - end - action :action do |data| - info "info" - warn "warn" - error "error" - respond foo: data[:foo], bar:'bar', some_var: @some_var - end - action :action_fail do |data| - fail "Something broke" - respond foo:'test' - end - action :action_varify do |data| - foo = data.varify(:foo,required:true) - end - listener :listener do - t=nil - start do |data| - t = Thread.new do - begin - sleep 0.1 - trigger foo:'bar', a:data[:a] - end while true - end - - respond started:'foo', c:data[:a] - end - stop do - info 'killing' - t.kill - respond done:true - end - end - end - @runtime = Factor::Connector::Runtime.new(MyDef) - end - - describe 'Actions' do - it 'can run and handle parameters' do - @runtime.run([:action], foo:'bar') - - expect(@runtime).to message info:'info' - expect(@runtime).to message warn:'warn' - expect(@runtime).to respond foo:'bar', bar:'bar', some_var:'some_var' - end - - it 'can fail' do - @runtime.run([:action_fail], foo:'bar') - expect(@runtime).to fail 'Something broke' - end - - it 'can fail' do - @runtime.run([:action_varify], nofoo:'bar') - expect(@runtime).to fail 'Foo (:foo) is required' - end - end - - describe 'Listeners' do - it 'can start, stop, trigger and handle parameters' do - @runtime.start_listener([:listener], a:'b') - expect(@runtime).to respond started:'foo', c:'b' - expect(@runtime).to trigger foo:'bar', a:'b' - @runtime.stop_listener - expect(@runtime).to message info:'killing' - expect(@runtime).to respond done:true - end - end -end diff --git a/spec/connector_spec.rb b/spec/connector_spec.rb new file mode 100644 index 0000000..f469816 --- /dev/null +++ b/spec/connector_spec.rb @@ -0,0 +1,11 @@ +# encoding: UTF-8 + +require 'spec_helper' + +require 'factor/commands/run_command' + +describe Factor::Connector do + it 'can' do + + end +end diff --git a/spec/logger_spec.rb b/spec/logger_spec.rb new file mode 100644 index 0000000..fb249a0 --- /dev/null +++ b/spec/logger_spec.rb @@ -0,0 +1,13 @@ +# encoding: UTF-8 + +require 'spec_helper' + +require 'factor/logger' + +describe Factor::Logger do + describe 'run' do + it 'can run a basic workflow' do + + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3a0f1c0..285170a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,19 +1,7 @@ # encoding: UTF-8 require 'coveralls' -require 'stringio' Coveralls.wear! $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib' - -def capture_stdout(&_block) - original_stdout = $stdout - $stdout = fake = StringIO.new - begin - yield - ensure - $stdout = original_stdout - end - fake.string -end diff --git a/spec/workflow/runtime_spec.rb b/spec/workflow/runtime_spec.rb deleted file mode 100644 index 57000b4..0000000 --- a/spec/workflow/runtime_spec.rb +++ /dev/null @@ -1,89 +0,0 @@ -# encoding: UTF-8 - -require 'spec_helper' - -require 'factor/workflow/runtime' -require 'factor/workflow/test' -require 'factor/connector/definition' -require 'factor/connector/registry' -require 'factor/logger/test' - -describe Factor::Workflow::Runtime do - before :all do - class MyDef < Factor::Connector::Definition - id :my_def - def initialize - @some_var='some_var' - end - action :action do |data| - info "info" - warn "warn" - error "error" - respond foo: data[:foo], bar:'bar', some_var: @some_var - end - action :action_fail do |data| - fail "Something broke" - respond foo:'test' - end - listener :listener do - t=nil - start do |data| - info 'i am starting' - t = Thread.new do - begin - trigger foo:'bar', a:data[:a] - sleep 5 - end while true - end - end - stop do - info 'killing' - t.kill - respond done:true - end - end - end - @logger = Factor::Log::TestLogger.new - @runtime = Factor::Workflow::Runtime.new({}, logger:@logger) - end - - before :each do - @logger.clear - end - - it 'can run a connector action' do - @runtime.run 'my_def::action', foo:'sweet' - - expect(@logger).to log success:'Starting' - expect(@logger).to log info:'info' - expect(@logger).to log warn:'warn' - expect(@logger).to log error:'error' - expect(@logger).to log success:'Completed' - end - - it 'can fail a connector action' do - @runtime.run 'my_def::action_fail', foo:'sweet' - - expect(@logger).to log error:'Failed: Something broke' - end - - it 'can run a workflow' do - workflow_definition = " - listen 'my_def::listener' do - info 'sweet' - end - " - @runtime.load(workflow_definition) - - expect(@logger).to log success:'Starting' - expect(@logger).to log info:'i am starting' - expect(@logger).to log success: 'Triggered' - expect(@logger).to log info: 'sweet' - - @runtime.unload - - expect(@logger).to log success:'Stopping' - expect(@logger).to log success:'Stopped' - - end -end diff --git a/spec/workflow/service_address_spec.rb b/spec/workflow/service_address_spec.rb deleted file mode 100644 index cb8a626..0000000 --- a/spec/workflow/service_address_spec.rb +++ /dev/null @@ -1,71 +0,0 @@ -# encoding: UTF-8 - -require 'spec_helper' - -require 'factor/workflow/service_address' - -describe Factor::Workflow::ServiceAddress do - - it 'fails on empty' do - expect { - Factor::Workflow::ServiceAddress.new('') - }.to raise_error - end - - it 'fails on no namespace' do - expect { - Factor::Workflow::ServiceAddress.new('foo') - }.to raise_error - end - - it 'fails on empty reference' do - expect{ - Factor::Workflow::ServiceAddress.new('::::') - }.to raise_error - end - - it 'can identify namespace' do - address = Factor::Workflow::ServiceAddress.new('a::b') - expect(address.namespace).to eq([:a]) - end - - it 'can identify deep namespace' do - address = Factor::Workflow::ServiceAddress.new('a::b::c') - expect(address.namespace).to eq([:a,:b]) - end - - it 'can identify service name' do - address = Factor::Workflow::ServiceAddress.new('a::b::c') - expect(address.service).to eq(:a) - end - - it 'can identify action/listener id' do - address = Factor::Workflow::ServiceAddress.new('a::b::c') - expect(address.id).to eq(:c) - end - - it 'can the resource path' do - address = Factor::Workflow::ServiceAddress.new('a::b::c::d') - expect(address.resource).to eq([:b,:c]) - end - - it 'can get the path' do - address = Factor::Workflow::ServiceAddress.new('a::b::c::d') - expect(address.path).to eq([:b,:c, :d]) - end - - it 'fails to get resource path if undefined' do - address = Factor::Workflow::ServiceAddress.new('a::b') - expect{ - address.resource - }.to raise_error - end - - it 'can initialize with array and convert to string' do - address = '' - expect { - address = Factor::Workflow::ServiceAddress.new([:a, :b, :c]) - }.to_not raise_error - expect(address.to_s).to eq('a::b::c') - end -end diff --git a/spec/workflow_spec.rb b/spec/workflow_spec.rb new file mode 100644 index 0000000..7e66fad --- /dev/null +++ b/spec/workflow_spec.rb @@ -0,0 +1,13 @@ +# encoding: UTF-8 + +require 'spec_helper' + +require 'factor/workflow/runtime' + +describe Factor::Workflow::Runtime do + describe 'run' do + it 'can run a basic workflow' do + + end + end +end From dc03bae2374377b7c1c25c5af711345b9f0e2b4c Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Sun, 17 Apr 2016 09:54:51 -0700 Subject: [PATCH 07/16] just stable releases --- .travis.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3f5baf5..a20957d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,8 @@ language: ruby rvm: -- 2.0.0 -- 2.1.3 -- 2.1.5 -- 2.2.0 -- 2.2.1 -- 2.2.2 +- 2.1.9 +- 2.2.4 +- 2.3.0 deploy: provider: rubygems gem: factor From a261f1fda2da10237619ba5dd1866065bdcbc29b Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Sun, 17 Apr 2016 11:13:10 -0700 Subject: [PATCH 08/16] More recent 2.1 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a20957d..77c147e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: ruby rvm: -- 2.1.9 +- 2.1.10 - 2.2.4 - 2.3.0 deploy: From c2e602661b2013d7d32f7d6113acdf7296716b74 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Sun, 17 Apr 2016 17:02:28 -0700 Subject: [PATCH 09/16] Only support 2.2+ --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 77c147e..ea7b8bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: ruby rvm: -- 2.1.10 - 2.2.4 - 2.3.0 deploy: From 7ff0959882880b0d454d961b45e3cd2f6d7e61a9 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Tue, 19 Apr 2016 18:32:28 -0700 Subject: [PATCH 10/16] Adding first parts of logging --- lib/factor/commands/workflow_command.rb | 4 +- lib/factor/logger.rb | 53 +++++++------------------ lib/factor/workflow/dsl.rb | 20 +++++++++- 3 files changed, 35 insertions(+), 42 deletions(-) diff --git a/lib/factor/commands/workflow_command.rb b/lib/factor/commands/workflow_command.rb index 0e7ce5b..445793f 100644 --- a/lib/factor/commands/workflow_command.rb +++ b/lib/factor/commands/workflow_command.rb @@ -2,6 +2,7 @@ require 'factor/commands/base' require 'factor/workflow/runtime' +require 'factor/logger' module Factor module Commands @@ -13,7 +14,8 @@ def run(args, options) load_settings(options) if options.settings workflow_definition = File.read(workflow_filename) - runtime = Factor::Workflow::Runtime.new(settings) + logger = Factor::Logger.new() + runtime = Factor::Workflow::Runtime.new(settings: settings, logger:logger) runtime.load workflow_definition, workflow_filename end end diff --git a/lib/factor/logger.rb b/lib/factor/logger.rb index 0c4d84d..3d19367 100644 --- a/lib/factor/logger.rb +++ b/lib/factor/logger.rb @@ -3,45 +3,32 @@ module Factor class Logger - attr_accessor :destination_stream - - def log(section, options={}) - options = { message: options } if options.is_a?(String) - tag = tag(options) - message = options['message'] || options[:message] - section_text = format_section(section) - write "[ #{section_text} ] [#{time}]#{tag} #{message}" if message - exception options[:exception] if options[:exception] + def log(log_level, message) + log_level_text = format_log_level(log_level) + puts "[ #{log_level_text} ] [#{time}] #{message}" end - def info(options = {}) - log :info, options + def info(message) + log :info, message end - def warn(options = {}) - log :warn, options + def warn(message) + log :warn, message end - def error(options = {}) - log :error, options + def error(message) + log :error, message end - def success(options = {}) - log :success, options + def success(message) + log :success, message end private - def exception(exception) - error message: " #{exception.message}" - exception.backtrace.each do |line| - error message: " #{line}" - end - end - - def format_section(section) - formated_section = section.to_s.upcase.center(10) - case section.to_sym + def format_log_level(log_level) + formated_log_level = log_level.to_s.upcase.center(10) + case log_level.to_sym when :error then Rainbow(formated_section).red when :info then Rainbow(formated_section).white.bright when :warn then Rainbow(formated_section).yellow @@ -50,18 +37,6 @@ def format_section(section) end end - def tag(options) - primary = options['service_id'] || options['instance_id'] - secondary = ":#{options['instane_id']}" if options['service_id'] && options['instance_id'] - primary ? "[#{primary}#{secondary || ''}]" : '' - end - - def write(message) - stream = @destination_stream || $stdout - stream.puts(message) - stream.flush - end - def time Time.now.localtime.strftime('%m/%d/%y %T.%L') end diff --git a/lib/factor/workflow/dsl.rb b/lib/factor/workflow/dsl.rb index 556ac6a..78e7326 100644 --- a/lib/factor/workflow/dsl.rb +++ b/lib/factor/workflow/dsl.rb @@ -4,10 +4,10 @@ module Factor module Workflow class DSL def initialize(options={}) - @options=options + @logger = options[:logger] end - def run(address,options={}, &block) + def run(address, options={}, &block) connector_class = Factor::Connector.get(address) connector = connector_class.new(options) @@ -23,6 +23,22 @@ def any(*events, &block) Future.any(*events, &block) end + def info(message) + @logger.info(message) if @logger + end + + def warn(message) + @logger.warn(message) if @logger + end + + def error(message) + @logger.error(message) if @logger + end + + def success(message) + @logger.success(message) if @logger + end + def on(type, *actions, &block) raise ArgumentError, "All actions must be an ConnectorFuture" unless actions.all? {|a| a.is_a?(ConnectorFuture) } actions.each {|a| a.on(type, &block) } From 51be56ea8a8aeb136a77b9c74b0d392082304aef Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Tue, 19 Apr 2016 22:55:50 -0700 Subject: [PATCH 11/16] Don't need that anymore --- lib/factor/workflow/dsl.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/factor/workflow/dsl.rb b/lib/factor/workflow/dsl.rb index 78e7326..0e48f56 100644 --- a/lib/factor/workflow/dsl.rb +++ b/lib/factor/workflow/dsl.rb @@ -7,7 +7,7 @@ def initialize(options={}) @logger = options[:logger] end - def run(address, options={}, &block) + def run(address, options={}) connector_class = Factor::Connector.get(address) connector = connector_class.new(options) From 020112fb9f335dd54b55215575f6c08a7179ee56 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Tue, 19 Apr 2016 22:56:03 -0700 Subject: [PATCH 12/16] Missed a renaming --- lib/factor/logger.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/factor/logger.rb b/lib/factor/logger.rb index 3d19367..5811be4 100644 --- a/lib/factor/logger.rb +++ b/lib/factor/logger.rb @@ -29,11 +29,11 @@ def success(message) def format_log_level(log_level) formated_log_level = log_level.to_s.upcase.center(10) case log_level.to_sym - when :error then Rainbow(formated_section).red - when :info then Rainbow(formated_section).white.bright - when :warn then Rainbow(formated_section).yellow - when :success then Rainbow(formated_section).green - else formated_section + when :error then Rainbow(formated_log_level).red + when :info then Rainbow(formated_log_level).white.bright + when :warn then Rainbow(formated_log_level).yellow + when :success then Rainbow(formated_log_level).green + else formated_log_level end end From 351d27b1616e477090e90e657911ea8ab99749b6 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Tue, 19 Apr 2016 23:06:06 -0700 Subject: [PATCH 13/16] Only needs to be accessible by children --- lib/factor/connector.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/factor/connector.rb b/lib/factor/connector.rb index acf947f..abb7d72 100644 --- a/lib/factor/connector.rb +++ b/lib/factor/connector.rb @@ -21,6 +21,8 @@ def self.get(path) def run end + protected + def trigger(data) changed notify_observers(:trigger, data) From 46861442fdca4bbab017ae163829ecd4539c4e13 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Tue, 19 Apr 2016 23:07:07 -0700 Subject: [PATCH 14/16] Little refactoring --- lib/factor/workflow/dsl.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/factor/workflow/dsl.rb b/lib/factor/workflow/dsl.rb index 0e48f56..ebcf6f3 100644 --- a/lib/factor/workflow/dsl.rb +++ b/lib/factor/workflow/dsl.rb @@ -24,19 +24,23 @@ def any(*events, &block) end def info(message) - @logger.info(message) if @logger + log(:info, message) end def warn(message) - @logger.warn(message) if @logger + log(:warn, message) end def error(message) - @logger.error(message) if @logger + log(:error, message) end def success(message) - @logger.success(message) if @logger + log(:success, message) + end + + def log(type, message) + @logger.log(type, message) if @logger end def on(type, *actions, &block) From 803c8910f1adbef8ea111cc5aaa6e9c81ace4e51 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Tue, 19 Apr 2016 23:07:23 -0700 Subject: [PATCH 15/16] Adds log messages/events to the connectors --- lib/factor/connector.rb | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/factor/connector.rb b/lib/factor/connector.rb index abb7d72..7b2cf13 100644 --- a/lib/factor/connector.rb +++ b/lib/factor/connector.rb @@ -17,7 +17,6 @@ def self.get(path) @@paths[path] end - def run end @@ -28,6 +27,29 @@ def trigger(data) notify_observers(:trigger, data) end + def info(message) + log(:info, message) + end + + def warn(message) + log(:warn, message) + end + + def success(message) + log(:success, message) + end + + def error(message) + log(:error, message) + end + + def log(type, message) + changed + notify_observers(:log, {type: type, message:message}) + changed + notify_observers(type, message) + end + private def self.underscore(string) From 52a4bfa192666e3d874d4367fbf021639b57a1a7 Mon Sep 17 00:00:00 2001 From: Maciej Skierkowski Date: Tue, 19 Apr 2016 23:48:00 -0700 Subject: [PATCH 16/16] closes #60 --- lib/commands.rb | 4 +++- lib/factor/commands/base.rb | 25 +++++++++++++++++++++++++ lib/factor/commands/run_command.rb | 25 ++++++++++++++++++++----- lib/factor/commands/workflow_command.rb | 17 +++++++++++++---- lib/factor/logger.rb | 7 ++++++- 5 files changed, 67 insertions(+), 11 deletions(-) diff --git a/lib/commands.rb b/lib/commands.rb index 5aaf593..890ad51 100644 --- a/lib/commands.rb +++ b/lib/commands.rb @@ -14,13 +14,15 @@ c.syntax = 'factor workflow workflow_file' c.description = 'Start the Factor.io Server in the current local directory' c.option '--settings FILE', String, 'factor.yml file path.' + c.option '--verbose', 'Verbose logging' c.when_called Factor::Commands::WorkflowCommand, :run end command 'run' do |c| c.syntax = 'factor run service_address params' c.description = 'Run a specific command.' - c.option '--require FILE', String, 'file to require for loading method' + c.option '--connector FILE', String, 'file to require for loading method' + c.option '--verbose', 'Verbose logging' c.when_called Factor::Commands::RunCommand, :run end diff --git a/lib/factor/commands/base.rb b/lib/factor/commands/base.rb index d1d0146..5e12777 100644 --- a/lib/factor/commands/base.rb +++ b/lib/factor/commands/base.rb @@ -13,6 +13,9 @@ class Command DEFAULT_FILENAME = File.expand_path('./settings.yml') + def initialize + @logger = Factor::Logger.new + end def load_settings(options = {}) relative_path = options.settings || DEFAULT_FILENAME @@ -26,6 +29,28 @@ def settings configatron.settings.to_hash end + protected + + def info(message) + log(:info, message) + end + + def warn(message) + log(:warn, message) + end + + def error(message) + log(:error, message) + end + + def success(message) + log(:success, message) + end + + def log(type, message) + @logger.log(type, message) if @logger + end + private diff --git a/lib/factor/commands/run_command.rb b/lib/factor/commands/run_command.rb index 5b53093..8403ab6 100644 --- a/lib/factor/commands/run_command.rb +++ b/lib/factor/commands/run_command.rb @@ -9,20 +9,35 @@ module Factor module Commands class RunCommand < Factor::Commands::Command def run(args, options) - address = args[0] - request_options = params(args[1..-1]) + parameters = params(args[1..-1]) - require options.require if options.require + if options.connector + info "Loading #{options.connector}" if options.verbose + require options.connector + end connector_class = Factor::Connector.get(address) raise ArgumentError, "Connector '#{address}' not found" unless connector_class - connector = connector_class.new(request_options) + info "Running '#{address}(#{parameters})'" if options.verbose + connector = connector_class.new(parameters) + connector.add_observer(self, :events) if options.verbose response = connector.run - puts response + @logger.indent += 1 + info response + @logger.indent -= 1 + success 'Done!' + end + + def events(type, content) + if type==:log + @logger.indent += 1 + @logger.log(content[:type], content[:message]) + @logger.indent -= 1 + end end end end diff --git a/lib/factor/commands/workflow_command.rb b/lib/factor/commands/workflow_command.rb index 445793f..07d3a72 100644 --- a/lib/factor/commands/workflow_command.rb +++ b/lib/factor/commands/workflow_command.rb @@ -9,14 +9,23 @@ module Commands # Workflow is a Command to start the factor runtime from the CLI class WorkflowCommand < Factor::Commands::Command def run(args, options) - workflow_filename = File.expand_path(args[0]) - load_settings(options) if options.settings + if options.settings + info "Loading settings from #{options.settings}" if options.verbose + load_settings(options) + end + workflow_filename = File.expand_path(args[0]) + info "Loading workflow from '#{workflow_filename}'" if options.verbose workflow_definition = File.read(workflow_filename) - logger = Factor::Logger.new() - runtime = Factor::Workflow::Runtime.new(settings: settings, logger:logger) + + info "Starting workflow runtime..." if options.verbose + @logger.indent += 1 if options.verbose + runtime = Factor::Workflow::Runtime.new(settings: settings, logger:@logger, verbose: options.verbose) runtime.load workflow_definition, workflow_filename + @logger.indent -= 1 if options.verbose + + success "Workflow completed" if options.verbose end end end diff --git a/lib/factor/logger.rb b/lib/factor/logger.rb index 5811be4..0e08158 100644 --- a/lib/factor/logger.rb +++ b/lib/factor/logger.rb @@ -2,10 +2,15 @@ module Factor class Logger + attr_accessor :indent + + def initialize + @indent = 0 + end def log(log_level, message) log_level_text = format_log_level(log_level) - puts "[ #{log_level_text} ] [#{time}] #{message}" + puts "[ #{log_level_text} ] [#{time}] #{' ' * @indent}#{message}" end def info(message)