speedflow 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +34 -0
- data/bin/console +7 -0
- data/bin/setup +8 -0
- data/bin/speedflow +25 -0
- data/lib/speedflow/command.rb +100 -0
- data/lib/speedflow/commands/flow.rb +106 -0
- data/lib/speedflow/commands/help.rb +45 -0
- data/lib/speedflow/configuration.rb +118 -0
- data/lib/speedflow/core_ext/hash.rb +79 -0
- data/lib/speedflow/flow.rb +82 -0
- data/lib/speedflow/message.rb +76 -0
- data/lib/speedflow/plugin_manager.rb +80 -0
- data/lib/speedflow/version.rb +3 -0
- data/lib/speedflow.rb +12 -0
- metadata +229 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5dbf2b0268766ac602d031b032f4a050893a0c70
|
4
|
+
data.tar.gz: b80f540563d169c40148ec7ec5345566e8c230fc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: de2a65663c9b74ed32dd28e5f5273aa472fd3137fbb25a799cb8ebd97525b99c17e72b0db16669a73d774bdfa93c84857bd63233fc54d16ae0b6e816c14615fa
|
7
|
+
data.tar.gz: 221cd775cb9bbfc581a39a0b6a0b3717f12c8cd769c8698fa103b91080fdf5e5e4db4007fbe869cbd196d705fab3de7ba21ea2891d82567ed573448295f753af
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Speedflow
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# <img src="https://raw.githubusercontent.com/speedflow/speedflow/master/resources/logo.png" alt="Speedflow" height="40" />
|
2
|
+
|
3
|
+
[](https://travis-ci.org/speedflow/speedflow)
|
4
|
+
[](https://badge.fury.io/rb/speedflow)
|
5
|
+
[](https://codeclimate.com/github/speedflow/speedflow)
|
6
|
+
[](https://codeclimate.com/github/speedflow/speedflow/coverage)
|
7
|
+
[](http://inch-ci.org/github/speedflow/speedflow)
|
8
|
+
|
9
|
+
|
10
|
+
Speedflow provides a robust system to transform your painful workflow :rage: in a sweet workflow :relaxed:.
|
11
|
+
Several [plugins](https://github.com/speedflow?utf8=%E2%9C%93&query=speedflow-) :package: are availables to help you to keep your time :clock1:.
|
12
|
+
|
13
|
+
## Philosophy
|
14
|
+
|
15
|
+
When you work with some different tools, the workflow can be transform to a Rube Goldberg machine... and can be very painful.
|
16
|
+
The idea of Speedflow™ is very simple, he offer a glue between different tools like Jira, Trello, Git and others...
|
17
|
+
|
18
|
+
## Getting Started
|
19
|
+
|
20
|
+
* [Install](https://github.com/speedflow/speedflow/wiki/Installation) the gem
|
21
|
+
* [Install](https://github.com/speedflow/speedflow/wiki/Plugins) the plugins
|
22
|
+
|
23
|
+
## The Core Speedflow Team
|
24
|
+
|
25
|
+
* [Julien Breux](https://github.com/JulienBreux) - Author
|
26
|
+
* [Emmanuel Julliot](https://github.com/emmanueljulliot) - Logo Designer
|
27
|
+
|
28
|
+
## Donate
|
29
|
+
|
30
|
+
I maintain this project in my free time, if it helped you please support my work [via paypal](https://paypal.me/JulienBreux), thanks a lot!
|
31
|
+
|
32
|
+
## License
|
33
|
+
|
34
|
+
See [LICENSE](https://github.com/speedflow/speedflow/blob/master/LICENSE).
|
data/bin/console
ADDED
data/bin/setup
ADDED
data/bin/speedflow
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
STDOUT.sync = true
|
3
|
+
|
4
|
+
require 'mercenary'
|
5
|
+
require 'speedflow'
|
6
|
+
|
7
|
+
# Speedflow program
|
8
|
+
Mercenary.program(:speedflow) do |program|
|
9
|
+
program.version Speedflow::VERSION
|
10
|
+
program.description 'Speedflow is a CLI tool to help you to keep your time.'
|
11
|
+
program.syntax "speedflow [OPTIONS] COMMAND [arg...]\n" \
|
12
|
+
' speedflow [ --help | -v | --version | --trace ]'
|
13
|
+
program.option(
|
14
|
+
'config',
|
15
|
+
'-c FILE1[,FILE2[,FILE3...]]',
|
16
|
+
'--config FILE1[,FILE2[,FILE3...]]',
|
17
|
+
Array,
|
18
|
+
'Custom configuration file')
|
19
|
+
|
20
|
+
# program.logger(Logger::DEBUG)
|
21
|
+
|
22
|
+
Speedflow::Command.subclasses.each { |c| c.init_with_program(program) }
|
23
|
+
|
24
|
+
program.default_command(:help)
|
25
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Speedflow
|
2
|
+
# Used to manage the commands
|
3
|
+
class Command
|
4
|
+
class << self
|
5
|
+
include Message
|
6
|
+
|
7
|
+
# @return [Hash] List of arguments
|
8
|
+
attr_writer :argv
|
9
|
+
|
10
|
+
# A list of subclasses of Speedflow::Command
|
11
|
+
#
|
12
|
+
# Returns Array of commands.
|
13
|
+
def subclasses
|
14
|
+
@subclasses ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
# Keep a list of subclasses of Speedflow::Command every time it's
|
18
|
+
# inherited. Called automatically.
|
19
|
+
#
|
20
|
+
# base - the subclass
|
21
|
+
#
|
22
|
+
# Returns nothing.
|
23
|
+
def inherited(base)
|
24
|
+
subclasses << base
|
25
|
+
super(base)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Speedflow configuration with the options passed in as overrides
|
29
|
+
#
|
30
|
+
# options - the configuration overrides
|
31
|
+
#
|
32
|
+
# Returns a full Speedflow configuration.
|
33
|
+
def config_from_options(options)
|
34
|
+
Speedflow::Configuration.get(options)
|
35
|
+
rescue ConfigurationFileNotFound => exception
|
36
|
+
error exception.message
|
37
|
+
abort
|
38
|
+
rescue ConfigurationFileFormat => exception
|
39
|
+
error exception.message
|
40
|
+
abort
|
41
|
+
end
|
42
|
+
|
43
|
+
# Public: Get config from fresh options
|
44
|
+
#
|
45
|
+
# program - Program.
|
46
|
+
# command - Commad.
|
47
|
+
# options - Options.
|
48
|
+
#
|
49
|
+
# Returns configuration Hash.
|
50
|
+
def config_from_fresh_options(program, command, options = {})
|
51
|
+
execute_from_option(program, command, 'config') do |value|
|
52
|
+
options = { config: value }
|
53
|
+
end
|
54
|
+
|
55
|
+
config_from_options(options)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Public: Exectute option from key
|
59
|
+
#
|
60
|
+
# prog - Program.
|
61
|
+
# command - Commad.
|
62
|
+
# opt_key - Option key.
|
63
|
+
#
|
64
|
+
# Returns nothing.
|
65
|
+
def execute_from_option(prog, command, opt_key)
|
66
|
+
opt_index = prog.options.find_index { |o| o.config_key == opt_key }
|
67
|
+
opt = prog.options[opt_index]
|
68
|
+
opt_parser = OptionParser.new do |opts|
|
69
|
+
command.add_default_options(opts)
|
70
|
+
opts.on(*opt.for_option_parser) { |value| yield(value) }
|
71
|
+
end
|
72
|
+
|
73
|
+
# rubocop:disable HandleExceptions
|
74
|
+
begin
|
75
|
+
opt_parser.parse(argv)
|
76
|
+
rescue OptionParser::InvalidOption
|
77
|
+
# Nothing.
|
78
|
+
end
|
79
|
+
# rubocop:enable HandleExceptions
|
80
|
+
end
|
81
|
+
|
82
|
+
# Display invalid command.
|
83
|
+
#
|
84
|
+
# cmd - the command
|
85
|
+
#
|
86
|
+
# Returns nothing
|
87
|
+
def invalid_command(cmd)
|
88
|
+
error "Command not found '#{cmd}'."
|
89
|
+
notice 'Please, use --help to display valid commands.'
|
90
|
+
end
|
91
|
+
|
92
|
+
# Public: Define argv (used to test)
|
93
|
+
#
|
94
|
+
# Returns Array of argv.
|
95
|
+
def argv
|
96
|
+
@argv ||= ARGV
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Speedflow
|
2
|
+
module Commands
|
3
|
+
# Flow command
|
4
|
+
class Flow < Command
|
5
|
+
class << self
|
6
|
+
include Message
|
7
|
+
|
8
|
+
# Init command with program.
|
9
|
+
#
|
10
|
+
# prog - the program.
|
11
|
+
#
|
12
|
+
# Returns nothing.
|
13
|
+
def init_with_program(program)
|
14
|
+
program.command(:flow) do |command|
|
15
|
+
command.syntax 'flow [trigger]'
|
16
|
+
command.description 'Play with the flow, trigger some actions.'
|
17
|
+
|
18
|
+
add_options_from_flow(program, command)
|
19
|
+
|
20
|
+
command.action do |args, options|
|
21
|
+
config = config_from_options(options)
|
22
|
+
trigger = (args.first || '').to_sym
|
23
|
+
process(trigger, config, args, options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Process command.
|
29
|
+
#
|
30
|
+
# trigger - Trigger name.
|
31
|
+
# config - Configuration.
|
32
|
+
# args - Arguments.
|
33
|
+
# options - Options.
|
34
|
+
#
|
35
|
+
# Returns nothing.
|
36
|
+
def process(trigger, config, args, options)
|
37
|
+
if args.empty? || !config.flow_triggers?
|
38
|
+
valid_triggers(config['flow'].keys)
|
39
|
+
elsif !config.flow_trigger?(trigger)
|
40
|
+
invalid_trigger(trigger)
|
41
|
+
valid_triggers(config['flow'].keys)
|
42
|
+
else
|
43
|
+
process_trigger(trigger, config, options)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Process trigger.
|
48
|
+
#
|
49
|
+
# trigger - Trigger name.
|
50
|
+
# config - Configuration.
|
51
|
+
# options - Options.
|
52
|
+
#
|
53
|
+
# Returns nothing.
|
54
|
+
def process_trigger(trigger, config, options)
|
55
|
+
flow = Speedflow::Flow.new(config)
|
56
|
+
begin
|
57
|
+
flow.trigger(trigger, options)
|
58
|
+
success "Trigger '#{trigger}' successful"
|
59
|
+
rescue FlowTriggerNotFound => exception
|
60
|
+
error "Trigger '#{trigger}' error: #{exception.message}"
|
61
|
+
rescue PluginNotFound => exception
|
62
|
+
error "Plugin error: #{exception.message}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Add options from flow arguents.
|
67
|
+
#
|
68
|
+
# program - the program.
|
69
|
+
# command - the command.
|
70
|
+
#
|
71
|
+
# Returns nothing.
|
72
|
+
def add_options_from_flow(program, command)
|
73
|
+
config = config_from_fresh_options(program, command)
|
74
|
+
flow = Speedflow::Flow.new(config)
|
75
|
+
flow.flat_arguments.each do |argument|
|
76
|
+
program.option(
|
77
|
+
argument,
|
78
|
+
'',
|
79
|
+
"--#{argument} #{argument.capitalize}",
|
80
|
+
String,
|
81
|
+
'Option form flow.')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Display invalid trigger message.
|
86
|
+
#
|
87
|
+
# trigger - the trigger name.
|
88
|
+
#
|
89
|
+
# Returns nothing.
|
90
|
+
def invalid_trigger(trigger)
|
91
|
+
error "Trigger not found '#{trigger}'."
|
92
|
+
end
|
93
|
+
|
94
|
+
# Display valid trigger message.
|
95
|
+
#
|
96
|
+
# triggers - Array of triggers.
|
97
|
+
#
|
98
|
+
# Returns nothing.
|
99
|
+
def valid_triggers(triggers)
|
100
|
+
info 'List of available triggers:'
|
101
|
+
message triggers.join(', ')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Speedflow
|
2
|
+
module Commands
|
3
|
+
# Help command
|
4
|
+
class Help < Command
|
5
|
+
class << self
|
6
|
+
include Message
|
7
|
+
|
8
|
+
# Init command with program.
|
9
|
+
#
|
10
|
+
# prog - the programm
|
11
|
+
#
|
12
|
+
# Returns nothing.
|
13
|
+
def init_with_program(prog)
|
14
|
+
prog.command(:help) do |c|
|
15
|
+
c.syntax 'help [subcommand]'
|
16
|
+
c.description 'Show the help message, optionally for a given ' \
|
17
|
+
'subcommand.'
|
18
|
+
c.action do |args, _|
|
19
|
+
cmd = (args.first || '').to_sym
|
20
|
+
process(cmd, prog, args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Process command.
|
26
|
+
#
|
27
|
+
# cmd - Command.
|
28
|
+
# prog - Program.
|
29
|
+
# args - Arguments.
|
30
|
+
#
|
31
|
+
# Returns nothing.
|
32
|
+
def process(cmd, prog, args)
|
33
|
+
if args.empty?
|
34
|
+
puts prog
|
35
|
+
elsif prog.has_command? cmd
|
36
|
+
puts prog.commands[cmd]
|
37
|
+
else
|
38
|
+
invalid_command(cmd)
|
39
|
+
abort
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Speedflow
|
2
|
+
# Used to manage configuration in Speedflow core
|
3
|
+
class Configuration < Hash
|
4
|
+
# Default configuration filename
|
5
|
+
DEFAULT_FILENAME = '.speedflow.yml'.freeze
|
6
|
+
|
7
|
+
# Default options. Overridden by values in .speedflow.yml.
|
8
|
+
DEFAULTS = Configuration[{
|
9
|
+
version: 1,
|
10
|
+
source: Dir.pwd,
|
11
|
+
plugins: [],
|
12
|
+
flow: {}
|
13
|
+
}]
|
14
|
+
|
15
|
+
# Public: Check triggers in flow
|
16
|
+
#
|
17
|
+
# Returns if flow has triggers.
|
18
|
+
def flow_triggers?
|
19
|
+
key?('flow') && !self['flow'].keys.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
# Public: Check trigger in flow
|
23
|
+
#
|
24
|
+
# trigger - Trigger name.
|
25
|
+
#
|
26
|
+
# Returns if trigger exists in flow.
|
27
|
+
def flow_trigger?(trigger)
|
28
|
+
key?('flow') && self['flow'].key?(trigger.to_s)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Directory of the Speedflow source folder
|
32
|
+
#
|
33
|
+
# override - the command-line options hash
|
34
|
+
#
|
35
|
+
# Returns the path to the Speedflow source directory.
|
36
|
+
def source(override)
|
37
|
+
get_config_value_with_override(:source, override)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Public: Get config value with override
|
41
|
+
#
|
42
|
+
# config_key - Configuration key.
|
43
|
+
# override - An Hash of override values.
|
44
|
+
#
|
45
|
+
# Returns the value of config key or override.
|
46
|
+
def get_config_value_with_override(config_key, override)
|
47
|
+
override[config_key] || self[config_key] || DEFAULTS[config_key]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Public: Generate list of configuration files from the override
|
51
|
+
#
|
52
|
+
# override - the command-line options hash
|
53
|
+
#
|
54
|
+
# Returns an Array of config files
|
55
|
+
def config_files(override)
|
56
|
+
files = []
|
57
|
+
if override.stringify_keys.key?('config')
|
58
|
+
files = override.stringify_keys.delete('config')
|
59
|
+
override.delete('config')
|
60
|
+
else
|
61
|
+
files << source(override) + File::SEPARATOR + DEFAULT_FILENAME
|
62
|
+
end
|
63
|
+
files = [files] unless files.is_a? Array
|
64
|
+
files
|
65
|
+
end
|
66
|
+
|
67
|
+
# Public: Read all files content
|
68
|
+
#
|
69
|
+
# files - the list of configuration file
|
70
|
+
#
|
71
|
+
# Returns the full configuration.
|
72
|
+
def read_config_files(files)
|
73
|
+
config = clone
|
74
|
+
|
75
|
+
files.each do |file|
|
76
|
+
unless File.exist?(file)
|
77
|
+
raise ConfigurationFileNotFound, "Unable to load config file: #{file}"
|
78
|
+
end
|
79
|
+
|
80
|
+
config = config.deep_merge(read_config_file(file))
|
81
|
+
end
|
82
|
+
|
83
|
+
config.replace_values_from_env
|
84
|
+
end
|
85
|
+
|
86
|
+
# Public: Read config file
|
87
|
+
#
|
88
|
+
# file - File to read.
|
89
|
+
#
|
90
|
+
# Returns a Hash.
|
91
|
+
def read_config_file(file)
|
92
|
+
SafeYAML.load_file(file) || {}
|
93
|
+
rescue
|
94
|
+
raise ConfigurationFileFormat, "This file is not a valid YAML: #{file}"
|
95
|
+
end
|
96
|
+
|
97
|
+
# Get all configuration.
|
98
|
+
#
|
99
|
+
# override - Hash of configuration.
|
100
|
+
#
|
101
|
+
# Returns the full configuration.
|
102
|
+
def self.get(override = {})
|
103
|
+
config = self[self::DEFAULTS]
|
104
|
+
override = self[override].stringify_keys
|
105
|
+
|
106
|
+
config = config.read_config_files(config.config_files(override))
|
107
|
+
config.deep_merge(override).stringify_keys
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# File not found exception
|
112
|
+
class ConfigurationFileNotFound < Exception
|
113
|
+
end
|
114
|
+
|
115
|
+
# File format exception
|
116
|
+
class ConfigurationFileFormat < Exception
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# Hash core extension
|
2
|
+
class Hash
|
3
|
+
# Public: Replace values from pattern.
|
4
|
+
#
|
5
|
+
# pattern - Used to define the pattern to match.
|
6
|
+
# hash - Default hash.
|
7
|
+
# block - Used to work outside
|
8
|
+
#
|
9
|
+
# TODO Re-enable Rubocop [...]
|
10
|
+
# rubocop:disable MethodLength, PerceivedComplexity, CyclomaticComplexity
|
11
|
+
#
|
12
|
+
# Returns Hash.
|
13
|
+
def replace_values(pattern, hash = nil, &block)
|
14
|
+
hash = self if hash.nil?
|
15
|
+
hash.each do |hash_key, hash_value|
|
16
|
+
if hash_value.is_a?(String) && hash_value =~ pattern
|
17
|
+
hash[hash_key] = yield(hash_value)
|
18
|
+
elsif hash_value.is_a?(Hash)
|
19
|
+
replace_values(pattern, hash_value, &block)
|
20
|
+
elsif hash_value.is_a?(Array)
|
21
|
+
hash_value.flatten.each_with_index do |array_v, array_i|
|
22
|
+
replace_values(pattern, array_v, &block) if array_v.is_a?(Hash)
|
23
|
+
hash_value[array_i] = yield(array_v) if array_v.is_a?(String)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
hash
|
28
|
+
end
|
29
|
+
# rubocop:enable MethodLength, PerceivedComplexity, CyclomaticComplexity
|
30
|
+
|
31
|
+
# Public: Change Hash values with environment values.
|
32
|
+
#
|
33
|
+
# Replace {ENV_VAR} by ENV['ENV_VAR'].
|
34
|
+
#
|
35
|
+
# hash - Hash to change (default is current Hash).
|
36
|
+
#
|
37
|
+
# Returns Hash.
|
38
|
+
def replace_values_from_env(hash = nil)
|
39
|
+
pattern = /\{([A-Z0-9_]+)\}/
|
40
|
+
replace_values(pattern, hash) do |value|
|
41
|
+
value.gsub(pattern) { |m| ENV[m.gsub(pattern, '\1')] }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: Replace values from previous values.
|
46
|
+
#
|
47
|
+
# prev_values - Hash of values.
|
48
|
+
# hash - Default hash.
|
49
|
+
#
|
50
|
+
# Returns Hash.
|
51
|
+
def replace_values_from_previous(prev_values, hash = nil)
|
52
|
+
pattern = /\{previous\.((?!\.)(?!.*\.\.)[A-Za-z0-9_\.]+(?<!\.))\}/
|
53
|
+
replace_values(pattern, hash) do |value|
|
54
|
+
value.gsub(pattern) do |m|
|
55
|
+
hash_values_from_keys_schema(m.gsub(pattern, '\1'), prev_values)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Public: Get hash value from keys schema
|
61
|
+
#
|
62
|
+
# keys - String of keys.
|
63
|
+
# hash - Hash to parse.
|
64
|
+
#
|
65
|
+
# Examples
|
66
|
+
#
|
67
|
+
# hash_values_from_keys_schema('foo.bar', {foo: {bar: 'qux'}})
|
68
|
+
# # => 'qux'
|
69
|
+
#
|
70
|
+
# Returns String value.
|
71
|
+
def hash_values_from_keys_schema(keys, hash)
|
72
|
+
value = {}
|
73
|
+
keys.split('.').each do |key, _|
|
74
|
+
value = value.stringify_keys[key] unless value.nil?
|
75
|
+
value = hash.stringify_keys[key] if value.nil?
|
76
|
+
end
|
77
|
+
value
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Speedflow
|
2
|
+
# Used to manage the flow of Speedflow
|
3
|
+
class Flow
|
4
|
+
# Public: Constructor.
|
5
|
+
#
|
6
|
+
# config - Default Hahs of config.
|
7
|
+
#
|
8
|
+
# Examples
|
9
|
+
#
|
10
|
+
# Flow.new({})
|
11
|
+
# # => <Speedflow::Flow>
|
12
|
+
#
|
13
|
+
# Returns a Hash of config.
|
14
|
+
def initialize(config)
|
15
|
+
@config = config
|
16
|
+
end
|
17
|
+
|
18
|
+
# Public: Trigger.
|
19
|
+
#
|
20
|
+
# trigger - Trigger name.
|
21
|
+
# inputs - Inputs.
|
22
|
+
#
|
23
|
+
# Returns nothing.
|
24
|
+
def trigger(trigger, inputs)
|
25
|
+
unless @config.flow_trigger?(trigger)
|
26
|
+
raise FlowTriggerNotFound, "Unable to trigger: #{trigger}"
|
27
|
+
end
|
28
|
+
|
29
|
+
output = {}
|
30
|
+
@config['flow'][trigger.to_s].each do |step|
|
31
|
+
arguments = step['arguments'] || {}
|
32
|
+
step['arguments'] = transform_arguments(arguments, output, inputs)
|
33
|
+
step['arguments']['_config'] = @config
|
34
|
+
output = plugin_manager.call_action_from_step(step)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Public: Transform arguments (to add value)
|
39
|
+
#
|
40
|
+
# arguments - Hash of arguments.
|
41
|
+
# prev_values - Hash of previous values.
|
42
|
+
# inputs - Hash of inputs.
|
43
|
+
#
|
44
|
+
# Returns Hash of arguments.
|
45
|
+
def transform_arguments(arguments, prev_values, inputs)
|
46
|
+
arguments = arguments.replace_values_from_previous(prev_values)
|
47
|
+
arguments.each do |arg_name, arg_values|
|
48
|
+
arguments[arg_name][:value] = ''
|
49
|
+
if inputs.key?(arg_name)
|
50
|
+
arguments[arg_name][:value] = inputs[arg_name]
|
51
|
+
elsif arg_values.key?('default')
|
52
|
+
arguments[arg_name][:value] = arg_values[:default]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
arguments
|
56
|
+
end
|
57
|
+
|
58
|
+
# Public: Get flat arguments from flow.
|
59
|
+
#
|
60
|
+
# Returns flat Array of arguments.
|
61
|
+
def flat_arguments
|
62
|
+
args = []
|
63
|
+
@config['flow'].each do |_, steps|
|
64
|
+
steps.each do |s|
|
65
|
+
(args << s['arguments'].keys).flatten! unless s['arguments'].nil?
|
66
|
+
end
|
67
|
+
end
|
68
|
+
args.uniq
|
69
|
+
end
|
70
|
+
|
71
|
+
# Plugin manager.
|
72
|
+
#
|
73
|
+
# Returns an instance of plugin manager.
|
74
|
+
def plugin_manager
|
75
|
+
@plugin_manager ||= PluginManager.new(@config['plugins'])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Trigger exception.
|
80
|
+
class FlowTriggerNotFound < Exception
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Speedflow core
|
2
|
+
module Speedflow
|
3
|
+
require 'colorize'
|
4
|
+
# Used to manage the output message
|
5
|
+
module Message
|
6
|
+
# Public: Message
|
7
|
+
#
|
8
|
+
# message - Message.
|
9
|
+
#
|
10
|
+
# Examples
|
11
|
+
#
|
12
|
+
# message 'Hello'
|
13
|
+
# # => nil
|
14
|
+
#
|
15
|
+
# Returns nothing.
|
16
|
+
def message(message)
|
17
|
+
puts message.colorize(:white)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Public: Notice
|
21
|
+
#
|
22
|
+
# message - Message.
|
23
|
+
#
|
24
|
+
# Examples
|
25
|
+
#
|
26
|
+
# notice 'Hello'
|
27
|
+
# # => nil
|
28
|
+
#
|
29
|
+
# Returns nothing.
|
30
|
+
def notice(message)
|
31
|
+
puts message.colorize(:grey)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Public: Info
|
35
|
+
#
|
36
|
+
# message - Message.
|
37
|
+
#
|
38
|
+
# Examples
|
39
|
+
#
|
40
|
+
# info 'Hello'
|
41
|
+
# # => nil
|
42
|
+
#
|
43
|
+
# Returns nothing.
|
44
|
+
def info(message)
|
45
|
+
puts message.colorize(:light_blue)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Public: Success
|
49
|
+
#
|
50
|
+
# message - Message.
|
51
|
+
#
|
52
|
+
# Examples
|
53
|
+
#
|
54
|
+
# success 'Ok'
|
55
|
+
# # => nil
|
56
|
+
#
|
57
|
+
# Returns nothing.
|
58
|
+
def success(message)
|
59
|
+
puts message.prepend('➜ ').colorize(:light_green)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Public: Error
|
63
|
+
#
|
64
|
+
# message - Message.
|
65
|
+
#
|
66
|
+
# Examples
|
67
|
+
#
|
68
|
+
# error 'Error'
|
69
|
+
# # => nil
|
70
|
+
#
|
71
|
+
# Returns nothing.
|
72
|
+
def error(message)
|
73
|
+
puts message.prepend('✗ ').colorize(:light_red)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Speedflow
|
2
|
+
# Used to manage the plugins
|
3
|
+
class PluginManager
|
4
|
+
include ActiveSupport::Inflector
|
5
|
+
include Message
|
6
|
+
|
7
|
+
PLUGIN_BASE = 'speedflow-plugin-'.freeze
|
8
|
+
|
9
|
+
# Public: Constructor
|
10
|
+
#
|
11
|
+
# plugins - Array of plugins
|
12
|
+
#
|
13
|
+
# Examples
|
14
|
+
#
|
15
|
+
# PluginManager.new({})
|
16
|
+
# # => <Speedflow::PluginManager>
|
17
|
+
#
|
18
|
+
# Returns an Arrays of plugins.
|
19
|
+
def initialize(plugins = [])
|
20
|
+
@plugins = plugins || []
|
21
|
+
load_plugins
|
22
|
+
end
|
23
|
+
|
24
|
+
# Public: Load plugins
|
25
|
+
#
|
26
|
+
# Returns nothing.
|
27
|
+
def load_plugins
|
28
|
+
@plugins.each { |plugin| require_plugin(plugin) }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Require a plugins (from Gem)
|
32
|
+
#
|
33
|
+
# Returns nothing.
|
34
|
+
def require_plugin(plugin)
|
35
|
+
require plugin.downcase.prepend(PLUGIN_BASE)
|
36
|
+
rescue LoadError
|
37
|
+
message = "Unable to load plugin '#{plugin}'.\n"
|
38
|
+
message << "Help: `gem install #{PLUGIN_BASE}#{plugin}`"
|
39
|
+
raise PluginNotFound, message
|
40
|
+
end
|
41
|
+
|
42
|
+
# Call action from flow step.
|
43
|
+
#
|
44
|
+
# step - Hash from flow. {plugin: '', action: '', arguments: ''}.
|
45
|
+
#
|
46
|
+
# Returns nothing.
|
47
|
+
def call_action_from_step(step)
|
48
|
+
step['arguments'] ||= {}
|
49
|
+
success "Run action '#{step['action']}' of '#{step['plugin']}' plugin."
|
50
|
+
call_plugin_action(step['plugin'], step['action'], step['arguments'])
|
51
|
+
end
|
52
|
+
|
53
|
+
# Call plugin action.
|
54
|
+
#
|
55
|
+
# plugin - Plugin name.
|
56
|
+
# action - Action name.
|
57
|
+
# arguments - List of arguments
|
58
|
+
#
|
59
|
+
# Returns nothing.
|
60
|
+
def call_plugin_action(plugin, action, arguments)
|
61
|
+
action = underscore(action.prepend('action_'))
|
62
|
+
module_name = plugin.downcase.capitalize.prepend('Speedflow::Plugin::')
|
63
|
+
begin
|
64
|
+
Kernel.const_get(module_name).send(action.to_s, arguments)
|
65
|
+
rescue NoMethodError => exception
|
66
|
+
message = "Unable to call action: #{module_name}.#{action}\n"
|
67
|
+
message << exception.message
|
68
|
+
raise PluginActionNotFound, message
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Plugin exception
|
74
|
+
class PluginNotFound < Exception
|
75
|
+
end
|
76
|
+
|
77
|
+
# Plugin action exception
|
78
|
+
class PluginActionNotFound < Exception
|
79
|
+
end
|
80
|
+
end
|
data/lib/speedflow.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'require_all'
|
2
|
+
require 'active_support/core_ext/hash'
|
3
|
+
require 'active_support/inflector'
|
4
|
+
require 'safe_yaml/load'
|
5
|
+
|
6
|
+
require_rel 'speedflow/*.rb'
|
7
|
+
require_rel 'speedflow/core_ext/*.rb'
|
8
|
+
require_rel 'speedflow/commands/*.rb'
|
9
|
+
|
10
|
+
# Speedflow core
|
11
|
+
module Speedflow
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,229 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: speedflow
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Julien Breux
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: require_all
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mercenary
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.3.5
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.3.5
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activesupport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: colorize
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: safe_yaml
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: speedflow-plugin-test
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: bundler
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.11'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.11'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rake
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '10.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '10.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '3.0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '3.0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: pry
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: codeclimate-test-reporter
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: simplecov
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description: Speedflow provides a robust system to transform yourpainful workflow
|
182
|
+
in a sweet workflow.
|
183
|
+
email:
|
184
|
+
- julien.breux@gmail.com
|
185
|
+
executables:
|
186
|
+
- speedflow
|
187
|
+
extensions: []
|
188
|
+
extra_rdoc_files: []
|
189
|
+
files:
|
190
|
+
- LICENSE
|
191
|
+
- README.md
|
192
|
+
- bin/console
|
193
|
+
- bin/setup
|
194
|
+
- bin/speedflow
|
195
|
+
- lib/speedflow.rb
|
196
|
+
- lib/speedflow/command.rb
|
197
|
+
- lib/speedflow/commands/flow.rb
|
198
|
+
- lib/speedflow/commands/help.rb
|
199
|
+
- lib/speedflow/configuration.rb
|
200
|
+
- lib/speedflow/core_ext/hash.rb
|
201
|
+
- lib/speedflow/flow.rb
|
202
|
+
- lib/speedflow/message.rb
|
203
|
+
- lib/speedflow/plugin_manager.rb
|
204
|
+
- lib/speedflow/version.rb
|
205
|
+
homepage: https://github.com/speedflow/speedflow
|
206
|
+
licenses:
|
207
|
+
- MIT
|
208
|
+
metadata: {}
|
209
|
+
post_install_message:
|
210
|
+
rdoc_options: []
|
211
|
+
require_paths:
|
212
|
+
- lib
|
213
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
214
|
+
requirements:
|
215
|
+
- - ">="
|
216
|
+
- !ruby/object:Gem::Version
|
217
|
+
version: '0'
|
218
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
223
|
+
requirements: []
|
224
|
+
rubyforge_project:
|
225
|
+
rubygems_version: 2.5.2
|
226
|
+
signing_key:
|
227
|
+
specification_version: 4
|
228
|
+
summary: Speedflow is a command-line tool to help you to keep your time.
|
229
|
+
test_files: []
|