superhosting 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +11 -0
  5. data/Gemfile +5 -0
  6. data/Gemfile.lock +117 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +23 -0
  9. data/Rakefile +6 -0
  10. data/Vagrantfile +10 -0
  11. data/bin/sx +10 -0
  12. data/bootstrap.sh +31 -0
  13. data/lib/superhosting.rb +40 -0
  14. data/lib/superhosting/base.rb +37 -0
  15. data/lib/superhosting/cli/base.rb +227 -0
  16. data/lib/superhosting/cli/cmd/admin_add.rb +11 -0
  17. data/lib/superhosting/cli/cmd/admin_container_add.rb +16 -0
  18. data/lib/superhosting/cli/cmd/admin_container_delete.rb +16 -0
  19. data/lib/superhosting/cli/cmd/admin_container_list.rb +12 -0
  20. data/lib/superhosting/cli/cmd/admin_delete.rb +11 -0
  21. data/lib/superhosting/cli/cmd/admin_passwd.rb +16 -0
  22. data/lib/superhosting/cli/cmd/container_add.rb +21 -0
  23. data/lib/superhosting/cli/cmd/container_admin_add.rb +16 -0
  24. data/lib/superhosting/cli/cmd/container_admin_delete.rb +16 -0
  25. data/lib/superhosting/cli/cmd/container_admin_list.rb +12 -0
  26. data/lib/superhosting/cli/cmd/container_change.rb +21 -0
  27. data/lib/superhosting/cli/cmd/container_delete.rb +11 -0
  28. data/lib/superhosting/cli/cmd/container_list.rb +9 -0
  29. data/lib/superhosting/cli/cmd/container_reconfig.rb +11 -0
  30. data/lib/superhosting/cli/cmd/container_restore.rb +24 -0
  31. data/lib/superhosting/cli/cmd/container_save.rb +14 -0
  32. data/lib/superhosting/cli/cmd/container_update.rb +11 -0
  33. data/lib/superhosting/cli/cmd/mysql_db_add.rb +9 -0
  34. data/lib/superhosting/cli/cmd/mysql_db_delete.rb +9 -0
  35. data/lib/superhosting/cli/cmd/mysql_db_dump.rb +9 -0
  36. data/lib/superhosting/cli/cmd/mysql_db_sql.rb +9 -0
  37. data/lib/superhosting/cli/cmd/mysql_grant.rb +9 -0
  38. data/lib/superhosting/cli/cmd/mysql_user_add.rb +12 -0
  39. data/lib/superhosting/cli/cmd/mysql_user_delete.rb +9 -0
  40. data/lib/superhosting/cli/cmd/site_add.rb +16 -0
  41. data/lib/superhosting/cli/cmd/site_alias_add.rb +16 -0
  42. data/lib/superhosting/cli/cmd/site_alias_delete.rb +16 -0
  43. data/lib/superhosting/cli/cmd/site_delete.rb +11 -0
  44. data/lib/superhosting/cli/cmd/site_rename.rb +16 -0
  45. data/lib/superhosting/cli/cmd/user_add.rb +24 -0
  46. data/lib/superhosting/cli/cmd/user_change.rb +24 -0
  47. data/lib/superhosting/cli/cmd/user_delete.rb +16 -0
  48. data/lib/superhosting/cli/cmd/user_list.rb +9 -0
  49. data/lib/superhosting/cli/cmd/user_passwd.rb +16 -0
  50. data/lib/superhosting/cli/error/ambiguous_command.rb +11 -0
  51. data/lib/superhosting/cli/error/base.rb +8 -0
  52. data/lib/superhosting/cli/error/controller.rb +8 -0
  53. data/lib/superhosting/controller/admin.rb +21 -0
  54. data/lib/superhosting/controller/admin/container.rb +24 -0
  55. data/lib/superhosting/controller/container.rb +174 -0
  56. data/lib/superhosting/controller/container/admin.rb +24 -0
  57. data/lib/superhosting/controller/mysql.rb +17 -0
  58. data/lib/superhosting/controller/mysql/db.rb +23 -0
  59. data/lib/superhosting/controller/mysql/user.rb +15 -0
  60. data/lib/superhosting/controller/site.rb +106 -0
  61. data/lib/superhosting/controller/site/alias.rb +21 -0
  62. data/lib/superhosting/controller/user.rb +25 -0
  63. data/lib/superhosting/docker_api.rb +31 -0
  64. data/lib/superhosting/helpers.rb +30 -0
  65. data/lib/superhosting/script_executor/base.rb +26 -0
  66. data/lib/superhosting/script_executor/container.rb +36 -0
  67. data/lib/superhosting/script_executor/site.rb +13 -0
  68. data/lib/superhosting/version.rb +3 -0
  69. data/superhosting.gemspec +32 -0
  70. metadata +341 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ N2ZhNTc2Yjg3MjdlM2M5ZTgzYzNiYWYwMmMyNDA5MTZjMDQ4NzQzZA==
5
+ data.tar.gz: !binary |-
6
+ ZDMwZmM4ZTQ1NzhiMWRmNTM2Y2I3MGQ5NjZjNWJkZjM0Yjk3OTA1NA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MGUxMWE4ODlkMjJkZDFmNjU3YmVmZjJmZWJkMzNhZmU1MTNhYTY3ZDY0ZDdj
10
+ MjEyNmM1ZTgxOGNmYWZhMzY3YzkxODlkMTkzYjVjMDUzZWExMjUyZTdiYzI5
11
+ MTdjOTY0NzIwMGY0MzNiZDE2OTY3ZDA5MmM3OTBlMmE5ZjQ2ZjU=
12
+ data.tar.gz: !binary |-
13
+ MTdmNDg1NDNiY2RjYjYwOTQzNjQ2N2FiNDk5YzE2OTUyN2NmYmE2MGM1NGNm
14
+ ZWJlNzNlODAwNjM2YTQxN2UxYmUwZWZjYTE3N2M1N2E4OTY5YzA0ZjhhMWYy
15
+ N2ZlNmM2ODM5ZGNiNzRjMWFhNTliZjZjNGM3YzAzYjc5OTNlNjU=
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ .idea
10
+ /.vagrant
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.1
4
+ deploy:
5
+ provider: rubygems
6
+ api_key:
7
+ secure: zVrSJRx992jsyfOywuEPsi/xJogvspgadByoZX4+2wVrvTtkA/6lNnutm7rKwOeO57GsX4siKsOQ43j1nT0N+gwjb/XKfn0yaev657eAC/1ybvZMu98dZ+J2FjAAouz4qqtfacAczf7mXf0Xi53K+az6GUMeuldxdDDyhm5Xr3ZhPqhueh7eIijNVdSx+vK78qTgRNjZFrXsqvN+SdS8ZT0ZPqvKcI6MarOppdpavCUbYWIyx6bOcKictc3CbS5XdM3n9iPM3ribhj/vqkX+nruRQf9oqq7ZnqU4oan3jdTlo3ZAdaiKWcDhjgzOBJkojUTEEgWGgnBFHZNNaRcdXGo9TADUcvM8fd/59eX71EuB3WL+kHgLKwkWT1/pnTuPk+PVJfR3df46UaEMQkF4b7fxigJ1sXzt+zJ3CJD+X5ejR+c8E4bYCxZXWFxhZhi+UG3sFJQVOIRJE8Hohs2gLdvxBYyjgrCCk+TJaCxgU1gg5CbfuQ05oblpNk12E2GwAa9iPue89X80GVvkO/twTeTN2LAw3lOacuacU+BIyzohly6t7V1YiNQq0p9Sqlvvnnrwkqgp1jAuD56tOeLzjTsEkrHnuGstIJnh+GzbsJLm/BcXCnLcdbdvpKn66Pt9z+RmsRfW01QptWpYx8I2DgJtVjWEYeDKwJCZ8/c0rmc=
8
+ gem: superhosting
9
+ on:
10
+ tags: true
11
+ repo: flant/superhosting
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'path_mapper', git: 'https://github.com/flant/path_mapper.git'
5
+ gem 'net_status', git: 'https://github.com/flant/net_status.git'
data/Gemfile.lock ADDED
@@ -0,0 +1,117 @@
1
+ GIT
2
+ remote: https://github.com/flant/net_status.git
3
+ revision: 50e9cceac16a288f9ffa396c070b2bbfe5f9d01d
4
+ specs:
5
+ net_status (0.0.2)
6
+
7
+ GIT
8
+ remote: https://github.com/flant/path_mapper.git
9
+ revision: 03d57883e9db7eeb46ef361b3c54002f86b78cd6
10
+ specs:
11
+ path_mapper (0.0.2)
12
+
13
+ PATH
14
+ remote: .
15
+ specs:
16
+ superhosting (0.0.1)
17
+ mixlib-cli (>= 1.5.0, < 2.0)
18
+ mixlib-shellout (>= 2.2.6, < 3.0)
19
+ net_status (>= 0.0.1, < 1.0)
20
+ path_mapper (>= 0.0.1, < 1.0)
21
+
22
+ GEM
23
+ remote: https://rubygems.org/
24
+ specs:
25
+ addressable (2.4.0)
26
+ backports (3.6.8)
27
+ binding_of_caller (0.7.2)
28
+ debug_inspector (>= 0.0.1)
29
+ byebug (8.2.2)
30
+ coderay (1.1.1)
31
+ debug_inspector (0.0.2)
32
+ diff-lcs (1.2.5)
33
+ ethon (0.8.1)
34
+ ffi (>= 1.3.0)
35
+ excon (0.47.0)
36
+ faraday (0.9.2)
37
+ multipart-post (>= 1.2, < 3)
38
+ faraday_middleware (0.10.0)
39
+ faraday (>= 0.7.4, < 0.10)
40
+ ffi (1.9.10)
41
+ gh (0.14.0)
42
+ addressable
43
+ backports
44
+ faraday (~> 0.8)
45
+ multi_json (~> 1.0)
46
+ net-http-persistent (>= 2.7)
47
+ net-http-pipeline
48
+ highline (1.7.8)
49
+ json (1.8.3)
50
+ launchy (2.4.3)
51
+ addressable (~> 2.3)
52
+ method_source (0.8.2)
53
+ mixlib-cli (1.5.0)
54
+ mixlib-shellout (2.2.6)
55
+ multi_json (1.11.2)
56
+ multipart-post (2.0.0)
57
+ net-http-persistent (2.9.4)
58
+ net-http-pipeline (1.0.1)
59
+ pry (0.10.3)
60
+ coderay (~> 1.1.0)
61
+ method_source (~> 0.8.1)
62
+ slop (~> 3.4)
63
+ pry-byebug (3.3.0)
64
+ byebug (~> 8.0)
65
+ pry (~> 0.10)
66
+ pry-stack_explorer (0.4.9.2)
67
+ binding_of_caller (>= 0.7)
68
+ pry (>= 0.9.11)
69
+ pusher-client (0.6.2)
70
+ json
71
+ websocket (~> 1.0)
72
+ rake (10.5.0)
73
+ rspec (3.4.0)
74
+ rspec-core (~> 3.4.0)
75
+ rspec-expectations (~> 3.4.0)
76
+ rspec-mocks (~> 3.4.0)
77
+ rspec-core (3.4.3)
78
+ rspec-support (~> 3.4.0)
79
+ rspec-expectations (3.4.0)
80
+ diff-lcs (>= 1.2.0, < 2.0)
81
+ rspec-support (~> 3.4.0)
82
+ rspec-mocks (3.4.1)
83
+ diff-lcs (>= 1.2.0, < 2.0)
84
+ rspec-support (~> 3.4.0)
85
+ rspec-support (3.4.1)
86
+ slop (3.6.0)
87
+ travis (1.8.2)
88
+ backports
89
+ faraday (~> 0.9)
90
+ faraday_middleware (~> 0.9, >= 0.9.1)
91
+ gh (~> 0.13)
92
+ highline (~> 1.6)
93
+ launchy (~> 2.1)
94
+ pusher-client (~> 0.4)
95
+ typhoeus (~> 0.6, >= 0.6.8)
96
+ typhoeus (0.8.0)
97
+ ethon (>= 0.8.0)
98
+ websocket (1.2.2)
99
+
100
+ PLATFORMS
101
+ ruby
102
+
103
+ DEPENDENCIES
104
+ bundler (~> 1.7)
105
+ excon (>= 0.45.4, < 1.0)
106
+ net_status!
107
+ path_mapper!
108
+ pry (>= 0.10.3, < 1.0)
109
+ pry-byebug (>= 3.3.0, < 4.0)
110
+ pry-stack_explorer (>= 0.4.9.2, < 1.0)
111
+ rake (~> 10.0)
112
+ rspec (~> 3.4, >= 3.4.0)
113
+ superhosting!
114
+ travis (~> 1.8, >= 1.8.2)
115
+
116
+ BUNDLED WITH
117
+ 1.11.2
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Alexey Igrychev
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # Superhosting
2
+
3
+ TODO
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'superhosting'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install superhosting
20
+
21
+ ## Usage
22
+
23
+ TODO
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/Vagrantfile ADDED
@@ -0,0 +1,10 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+ # vi: set sts=2 ts=2 sw=2 :
4
+
5
+ VAGRANTFILE_API_VERSION = "2"
6
+
7
+ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
8
+ config.vm.box = "ubuntu/trusty64"
9
+ config.vm.provision :shell, path: "bootstrap.sh"
10
+ end
data/bin/sx ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'superhosting'
4
+
5
+ begin
6
+ Superhosting::Cli::Base.start(ARGV)
7
+ rescue Superhosting::Cli::Error::Base => e
8
+ $stderr.puts([e.net_status[:error], e.net_status[:message]].compact.join(': '))
9
+ exit 1
10
+ end
data/bootstrap.sh ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env /bin/bash
2
+
3
+ apt-get update
4
+ apt-get install -y build-essential libpam0g-dev apt-transport-https ca-certificates
5
+ apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
6
+ echo 'deb https://apt.dockerproject.org/repo ubuntu-trusty main' > /etc/apt/sources.list.d/docker.list
7
+ apt-get update
8
+ apt-get install -y docker-engine
9
+
10
+ gpasswd -a vagrant docker
11
+ service docker restart
12
+
13
+ cd /tmp
14
+ git clone https://github.com/flant/pam_docker.git
15
+ cd pam_docker
16
+ make
17
+ make install-ubuntu-14.04
18
+
19
+ curl -sSL https://rvm.io/mpapis.asc | gpg --import -
20
+ curl -sSL https://get.rvm.io | sudo bash -s stable
21
+
22
+ echo 'source /etc/profile.d/rvm.sh' >> /etc/bash.bashrc
23
+ source /etc/profile.d/rvm.sh
24
+
25
+ rvm group add rvm vagrant
26
+ rvm install 2.2.1 --quiet-curl
27
+ rvm --default use 2.2.1
28
+ gem install bundler
29
+
30
+ cd /vagrant
31
+ bundle install
@@ -0,0 +1,40 @@
1
+ require 'mixlib/cli'
2
+ require 'mixlib/shellout'
3
+ require 'logger'
4
+ require 'pathname'
5
+ require 'excon'
6
+ require 'json'
7
+ require 'etc'
8
+ require 'erb'
9
+ require 'ostruct'
10
+ require 'pry-byebug'
11
+
12
+ require 'path_mapper'
13
+ require 'net_status'
14
+
15
+ require 'superhosting/version'
16
+ require 'superhosting/helpers'
17
+
18
+ require 'superhosting/base'
19
+ require 'superhosting/controller/admin'
20
+ require 'superhosting/controller/admin/container'
21
+ require 'superhosting/controller/container'
22
+ require 'superhosting/controller/container/admin'
23
+ require 'superhosting/controller/mysql'
24
+ require 'superhosting/controller/mysql/db'
25
+ require 'superhosting/controller/mysql/user'
26
+ require 'superhosting/controller/site'
27
+ require 'superhosting/controller/site/alias'
28
+ require 'superhosting/controller/user'
29
+
30
+ require 'superhosting/script_executor/base'
31
+ require 'superhosting/script_executor/container'
32
+ require 'superhosting/script_executor/site'
33
+
34
+ require 'superhosting/docker_api'
35
+
36
+ require 'superhosting/cli/error/base'
37
+ Dir["#{File.dirname(__FILE__)}/superhosting/cli/error/*.rb"].each{|cmd| require_relative cmd.split('.rb').first}
38
+
39
+ require 'superhosting/cli/base'
40
+ Dir["#{File.dirname(__FILE__)}/superhosting/cli/cmd/*.rb"].each{|cmd| require_relative cmd.split('.rb').first}
@@ -0,0 +1,37 @@
1
+ module Superhosting
2
+ class Base
3
+ include Helpers
4
+
5
+ def initialize(config_path: '/etc/sx', lib_path: '/var/lib/sx', logger: nil, docker_socket: nil)
6
+ @config_path = Pathname.new(config_path)
7
+ @lib_path = Pathname.new(lib_path)
8
+ @config = PathMapper.new(config_path)
9
+ @lib = PathMapper.new(lib_path)
10
+ @logger = logger
11
+
12
+ @docker_api = DockerApi.new(socket: docker_socket)
13
+ end
14
+
15
+ def debug(*a, &b)
16
+ @logger.debug(*a, &b) unless @logger.nil?
17
+ end
18
+
19
+ def command!(*command_args)
20
+ cmd = Mixlib::ShellOut.new(*command_args)
21
+ cmd.run_command
22
+ if cmd.status.success?
23
+ debug([cmd.stdout, cmd.stderr].join("\n"))
24
+ cmd
25
+ else
26
+ raise NetStatus::Exception.new(error: :error, message: [cmd.stdout, cmd.stderr].join("\n"))
27
+ end
28
+ end
29
+
30
+ def command(*command_args)
31
+ cmd = Mixlib::ShellOut.new(*command_args)
32
+ cmd.run_command
33
+ debug([cmd.stdout, cmd.stderr].join("\n"))
34
+ cmd
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,227 @@
1
+ module Superhosting
2
+ module Cli
3
+ module Cmd; end
4
+ class Base
5
+ include Mixlib::CLI
6
+
7
+ COMMANDS_MODULE = Cmd
8
+ CONTROLLERS_MODULE = Superhosting::Controller
9
+ CONTROLLER_BASE_OPTIONS = [:config_path, :lib_path, :docker_socket]
10
+
11
+ banner "#{?= * 50}\n#{?- * 19}SUPERHOSTING#{?- * 19}\n#{?= * 50}\n\n"
12
+
13
+ option :help,
14
+ :short => '-h',
15
+ :long => '--help',
16
+ :on => :tail
17
+
18
+ option :debug,
19
+ :long => '--debug',
20
+ :boolean => true,
21
+ :on => :tail
22
+
23
+ option :config_path,
24
+ :long => '--config-path PATH',
25
+ :on => :tail
26
+
27
+ option :lib_path,
28
+ :long => '--lib-path PATH',
29
+ :on => :tail
30
+
31
+ option :docker_socket,
32
+ :long => '--docker-socket PATH',
33
+ :on => :tail
34
+
35
+ def initialize(argv, node)
36
+ self.class.options.merge!(Base::options)
37
+ super()
38
+
39
+ @pos_args = parse_options(argv)
40
+ @node = node
41
+
42
+ @logger = Logger.new(STDOUT)
43
+ @logger.level = config[:debug] ? Logger::DEBUG : Logger::INFO
44
+ @logger.formatter = proc {|severity, datetime, progname, msg| sprintf("%s\n", msg.to_s.strip) }
45
+
46
+ self.help if config[:help] or self.class == Base
47
+ end
48
+
49
+ def help
50
+ def get_childs_banners(node)
51
+ if node.is_a? Hash
52
+ node.map do |k,v|
53
+ if v.is_a? Hash
54
+ get_childs_banners(node[k])
55
+ else
56
+ v.banner
57
+ end
58
+ end.join("\n")
59
+ else
60
+ node.banner
61
+ end
62
+ end
63
+
64
+ @logger.info("#{opt_parser.to_s}\n#{get_childs_banners(@node) if self.class == Base}")
65
+
66
+ exit 1
67
+ end
68
+
69
+ def run
70
+ begin
71
+ net_status = action
72
+ net_status ||= {}
73
+
74
+ raise Error::Controller, net_status unless net_status[:error].nil?
75
+ @logger.info(net_status[:data]) unless net_status[:data].nil?
76
+ @logger.debug('Done!')
77
+ rescue NetStatus::Exception => e
78
+ raise Error::Controller, e.net_status
79
+ end
80
+ end
81
+
82
+ def action
83
+ method = get_controller
84
+ opts = {}
85
+ method.parameters.each do |req, name|
86
+ if req.to_s.start_with? 'key'
87
+ opt = config[name]
88
+ self.help unless opt = @pos_args.shift if name == :name
89
+ opts.merge!(name => opt)
90
+ end
91
+ end
92
+ self.help unless @pos_args.empty? # only one position argument
93
+
94
+ method.parameters.empty? ? method.call : method.call(**opts)
95
+ end
96
+
97
+ def get_controller
98
+ def get_subcontroller_option
99
+ key = :"#{self.class.get_split_class_name.first}_name"
100
+ config[key] unless config[key].nil?
101
+ end
102
+
103
+ node = CONTROLLERS_MODULE
104
+ names = self.class.get_split_class_name
105
+
106
+ names.each do |n|
107
+ c_name = n.capitalize.to_sym
108
+ m_name = n.to_sym
109
+
110
+ if node.respond_to? :constants and node.constants.include? c_name
111
+ node = node.const_get(c_name)
112
+ elsif node.respond_to? :instance_methods and node.instance_methods(false).include? m_name
113
+ params = node.instance_method(:initialize).parameters
114
+
115
+ opts = {}
116
+ params.each do |req, name|
117
+ if req.to_s.start_with? 'key'
118
+ if name == :name
119
+ opt = get_subcontroller_option
120
+ elsif config.key? name
121
+ opt = config[name]
122
+ end
123
+ opts.merge!(name => opt) unless opt.nil?
124
+ end
125
+ end
126
+
127
+ CONTROLLER_BASE_OPTIONS.each {|opt| opts.merge!(opt => config[opt]) unless config[opt].nil? }
128
+ opts.merge!(logger: @logger)
129
+ return node.new(**opts).method(m_name)
130
+ end
131
+ end
132
+ raise Error::Base.new('Method doesn\'t found')
133
+ end
134
+
135
+ class << self
136
+ def start(args)
137
+ def clear_args(args, cmd)
138
+ split_toggle_case_name(cmd).length.times{ args.shift }
139
+ args
140
+ end
141
+
142
+ prepend
143
+ cmd, node = get_cmd_and_node(args)
144
+ args = clear_args(args, cmd)
145
+ cmd.new(args, node).run
146
+ end
147
+
148
+ def prepend
149
+ set_commands_hierarchy
150
+ set_banners
151
+ end
152
+
153
+ def set_banners(node=@@commands_hierarchy, path=[])
154
+ node.each do |k,v|
155
+ path_ = path.dup
156
+ path_ << k
157
+ if v.is_a? Hash
158
+ set_banners(v, path_)
159
+ else
160
+ v.banner("sx #{path_.join(' ')}#{" <#{path.last}>" if v.has_required_param?}#{' (options)' unless v.options.empty?}")
161
+ end
162
+ end
163
+ end
164
+
165
+ def has_required_param?
166
+ false
167
+ end
168
+
169
+ def set_commands_hierarchy
170
+ def get_commands
171
+ COMMANDS_MODULE.constants.select {|c| Class === COMMANDS_MODULE.const_get(c) }.sort
172
+ end
173
+
174
+ @@commands_hierarchy = get_commands.inject({}) do |h,k|
175
+ node = h
176
+ parts = split_toggle_case_name(k)
177
+ parts.each do |cmd|
178
+ node = (node[cmd] ||= (cmd == parts.last) ? COMMANDS_MODULE.const_get(k) : {})
179
+ end
180
+ h
181
+ end
182
+ end
183
+
184
+ def get_split_class_name
185
+ self.split_toggle_case_name(self.name.split('::').last)
186
+ end
187
+
188
+ def split_toggle_case_name(klass)
189
+ klass.to_s.gsub(/([[:lower:]])([[:upper:]])/, '\1 \2').split(' ').map(&:downcase)
190
+ end
191
+
192
+ def get_cmd_and_node(args)
193
+ def positional_arguments(args)
194
+ args.select { |arg| arg =~ /^([[:alnum:]\_\-]+)$/ }
195
+ end
196
+
197
+ args = positional_arguments(args)
198
+ node = @@commands_hierarchy
199
+ path = []
200
+ key = ''
201
+ cmd = nil
202
+ while arg = args.shift and cmd.nil?
203
+ res = node.keys.select { |k| k.start_with? arg }
204
+
205
+ case res.count
206
+ when 1
207
+ key = res.first
208
+ cmd = node[key] if node[key].is_a? Class
209
+ when 0
210
+ break
211
+ else
212
+ raise Error::AmbiguousCommand.new(path: path, commands: res)
213
+ end
214
+
215
+ path << key
216
+ node = node[key]
217
+ end
218
+
219
+ cmd ||= self
220
+ node = { key => node }
221
+
222
+ [cmd, node]
223
+ end
224
+ end
225
+ end
226
+ end
227
+ end