tomo 1.3.0 → 1.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '098582c6866a637c63527b1bf1fecd347686cf1472ddf4e93b11002180b4e0c3'
4
- data.tar.gz: 74ea3479cd90f9d3cf038d7b8e6cffa0200a98a51562798ba1b852ca66866f4e
3
+ metadata.gz: c6e4c4920127adec0015456e7b666ecf2f58f7459c347d62b04ad337cdf8818c
4
+ data.tar.gz: e153c6374dcd288ce08125c70c973fb60f4f9d389020f19261d63dba97b1e925
5
5
  SHA512:
6
- metadata.gz: 7ddd09600b3eae9914c7641b08f169f7f802297cf76170b7faffc0d0cb75cd0af22071c5e8d358c244464efe00672d4573054f30a2947b258aaef3af98e9ed74
7
- data.tar.gz: 78c334db75ce535dd00c434fb3a4a927af4369d7f747cc3776334146c9c41bbae785ddb15e4ec42bdea59a66223a3836644f6bf0ec74e42f57c6de85fc822025
6
+ metadata.gz: eb670d2ab71b34098caee98bf44d47cc6a6a7c2e857541d33b9394588b4b294375ab91dbd1c071080fc5f70d893acf80acca09b1a863e99ad7c7994f6e7c6991
7
+ data.tar.gz: 14082e3582721a2816cfee25b8a613a8fc267e97e4b317b1cc87833f7150dd9ecee3af751ba123d4da06ea515d9d1913bda80cd9187311a9e9ab636a31b0b48e
data/README.md CHANGED
@@ -1,26 +1,26 @@
1
1
  # Tomo
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/tomo.svg)](https://rubygems.org/gems/tomo)
4
- [![Travis](https://img.shields.io/travis/mattbrictson/tomo.svg?label=travis)](https://travis-ci.org/mattbrictson/tomo)
4
+ [![Travis](https://img.shields.io/travis/com/mattbrictson/tomo.svg?label=travis)](https://travis-ci.com/github/mattbrictson/tomo)
5
5
  [![Circle](https://circleci.com/gh/mattbrictson/tomo/tree/main.svg?style=shield)](https://app.circleci.com/pipelines/github/mattbrictson/tomo?branch=main)
6
6
  [![Code Climate](https://codeclimate.com/github/mattbrictson/tomo/badges/gpa.svg)](https://codeclimate.com/github/mattbrictson/tomo)
7
7
 
8
- Tomo is a friendly command-line tool for deploying Rails apps. It is a new alternative to Capistrano, Mina, and Shipit that aims for simplicity and developer happiness.
8
+ Tomo is a friendly command-line tool for deploying Rails apps.
9
9
 
10
10
  💻 Rich command-line interface with built-in bash completions<br/>
11
11
  ☁️ Multi-environment and role-based multi-host support<br/>
12
12
  💎 Everything you need to deploy a basic Rails app out of the box<br/>
13
13
  🔌 Easily extensible for polyglot projects (not just Rails!)<br/>
14
- 💡 Concise, helpful error messages<br/>
15
14
  📚 Quality documentation<br/>
16
15
  🔬 Minimal dependencies<br/>
17
16
 
18
- See [how tomo compares](https://tomo-deploy.com/comparisons/) to other Ruby deployment tools.
17
+ [→ See how tomo compares to other Ruby deployment tools like Capistrano and Mina.](https://tomo-deploy.com/comparisons/)
19
18
 
20
19
  ---
21
20
 
22
21
  - [Quick start](#quick-start)
23
22
  - [Usage](#usage)
23
+ - [Extending tomo](#extending-tomo)
24
24
  - [Tutorials](#tutorials)
25
25
  - [Reference documentation](#reference-documentation)
26
26
  - [FAQ](#faq)
@@ -31,21 +31,19 @@ See [how tomo compares](https://tomo-deploy.com/comparisons/) to other Ruby depl
31
31
 
32
32
  ## Quick start
33
33
 
34
+ #### Installation
35
+
34
36
  Tomo is distributed as a ruby gem. To install:
35
37
 
36
38
  ```
37
39
  $ gem install tomo
38
40
  ```
39
41
 
40
- For instructions on setting up bash completions, run:
41
-
42
- ```
43
- $ tomo completion-script
44
- ```
42
+ > 💡 **Protip:** run `tomo completion-script` for instructions on setting up bash completions.
45
43
 
46
44
  #### Configuring a project
47
45
 
48
- Tomo is configured via a `.tomo/config.rb` file in your project. To get started, you can use `tomo init` to generate a configuration that works for a basic Rails app.
46
+ Tomo is configured via a `.tomo/config.rb` file in your project. To get started, run `tomo init` to generate a configuration that works for a basic Rails app.
49
47
 
50
48
  ![$ tomo init](./readme_images/tomo-init.png)
51
49
 
@@ -87,20 +85,25 @@ deploy do
87
85
  end
88
86
  ```
89
87
 
90
- Check out the [configuration docs](https://tomo-deploy.com/configuration/) for a complete reference.
88
+ #### Next steps
89
+
90
+ [→ The reference docs have a complete guide to tomo configuration.](https://tomo-deploy.com/configuration/)<br>
91
+ [→ Check out the **Deploying Rails From Scratch** tutorial for a step-by-step guide to using tomo with a real app.](https://tomo-deploy.com/tutorials/deploying-rails-from-scratch/)
91
92
 
92
93
  ## Usage
93
94
 
94
- Tomo gives you easy-to-use commands for three common use cases:
95
+ Once your project is configured, you can:
96
+
97
+ 1. Run `tomo setup` to prepare the remote host for its first deploy.
98
+ 2. Run `tomo deploy` to deploy your app.
99
+ 3. Use `tomo run` to invoke one-off tasks, like launching a Rails console.
95
100
 
96
- 1. `tomo setup` prepares a remote host for its first deploy
97
- 2. `tomo deploy` performs a deployment
98
- 3. `tomo run` lets you invoke one-off tasks
101
+ > 💡 **Protip:** add `-h` or `--help` when running any of these commands to see detailed docs and examples.
99
102
 
100
- ### Setup
103
+ ### `tomo setup`
101
104
 
102
105
  `tomo setup` prepares the remote host for its first deploy by sequentially running the
103
- [setup](https://tomo-deploy.com/configuration#setupblock) list of tasks specified in `.tomo/config.rb`. These tasks typically create directories, initialize data stores, install prerequisite tools, and perform other one-time actions that are necessary before a deploy can take place.
106
+ `setup` list of tasks specified in `.tomo/config.rb`. These tasks typically create directories, initialize data stores, install prerequisite tools, and perform other one-time actions that are necessary before a deploy can take place.
104
107
 
105
108
  Out of the box, tomo will:
106
109
 
@@ -109,9 +112,12 @@ Out of the box, tomo will:
109
112
  - Create all necessary deployment directories
110
113
  - Create the Rails database, load the schema, and insert seed data
111
114
 
112
- ### Deploy
115
+ [→ Here is the default list of tasks invoked by the setup command.](https://tomo-deploy.com/configuration#setupblock)<br>
116
+ [→ The `tomo setup` section of the reference docs explains supported command-line options.](https://tomo-deploy.com/commands/setup/)
113
117
 
114
- Whereas `tomo setup` is typically run once, you can use `tomo deploy` every time you want to deploy a new version of your app. The deploy command will sequentially run the [deploy](https://tomo-deploy.com/configuration#deployblock) list of tasks specified in `.tomo/config.rb`. You can customize this list to meet the needs of your app. By default, tomo runs these tasks:
118
+ ### `tomo deploy`
119
+
120
+ Whereas `tomo setup` is typically run once, you can use `tomo deploy` every time you want to deploy a new version of your app. The deploy command will sequentially run the `deploy` list of tasks specified in `.tomo/config.rb`. You can customize this list to meet the needs of your app. By default, tomo runs these tasks:
115
121
 
116
122
  1. Create a release (using the [git:create_release](https://tomo-deploy.com/plugins/git#gitcreate_release) task)
117
123
  2. Build the project (e.g. [bundler:install](https://tomo-deploy.com/plugins/bundler#bundlerinstall), [rails:assets_precompile](https://tomo-deploy.com/plugins/rails#railsassets_precompile))
@@ -120,7 +126,13 @@ Whereas `tomo setup` is typically run once, you can use `tomo deploy` every time
120
126
  5. Restart the app to use the new current release (e.g. [puma:restart](https://tomo-deploy.com/plugins/puma#pumarestart))
121
127
  6. Perform any cleanup (e.g. [bundler:clean](https://tomo-deploy.com/plugins/bundler#bundlerclean))
122
128
 
123
- ### Run
129
+ > 💡 **Protip:** you can abbreviate tomo commands, like `tomo d` for `tomo deploy` or `tomo s` for `tomo setup`.
130
+
131
+ [→ Here is the default list of tasks invoked by the deploy command.](https://tomo-deploy.com/configuration#deployblock)<br>
132
+ [→ The `tomo deploy` section of the reference docs explains supported command-line options, like `--dry-run`.](https://tomo-deploy.com/commands/deploy/)
133
+
134
+
135
+ ### `tomo run [TASK]`
124
136
 
125
137
  Tomo can also `run` individual remote tasks on demand. You can use the `tasks` command to see the list of tasks tomo knows about.
126
138
 
@@ -130,9 +142,15 @@ One of the built-in Rails tasks is `rails:console`, which brings up a fully-inte
130
142
 
131
143
  ![$ tomo run rails:console](./readme_images/tomo-run-rails-console.png)
132
144
 
133
- ### Extending tomo
145
+ > 💡 **Protip:** you can shorten this as `tomo rails:console` (the `run` command is implied).
146
+
147
+ [→ The `tomo run` section of the reference docs explains supported command-line options and has more examples.](https://tomo-deploy.com/commands/run/)
148
+
134
149
 
135
- Tomo has many plugins built-in, but you can easily add your own to extend tomo with custom tasks. By convention, custom plugins are stored in `.tomo/plugins/`. These plugins can define tasks as plain ruby methods. For example:
150
+
151
+ ## Extending tomo
152
+
153
+ Tomo has a powerful plugin system that lets you extend tomo by installing Ruby gems (e.g. [tomo-plugin-sidekiq](https://github.com/mattbrictson/tomo-plugin-sidekiq)). You can also define plugins on the fly within your project by adding simple `.rb` files to `.tomo/plugins/`. These plugins can define tasks as plain ruby methods. For example:
136
154
 
137
155
  ```ruby
138
156
  # .tomo/plugins/my-plugin.rb
@@ -142,8 +160,6 @@ def hello
142
160
  end
143
161
  ```
144
162
 
145
- Use `remote.run` to execute shell scripts on the remote host, similar to how you would use Ruby's `system`. Project settings are accessible via `settings`, which is a plain Ruby hash.
146
-
147
163
  Load your plugin in `config.rb` like this:
148
164
 
149
165
  ```ruby
@@ -156,7 +172,9 @@ And run it!
156
172
 
157
173
  ![$ tomo run my-plugin:hello](./readme_images/tomo-run-hello.png)
158
174
 
159
- Read the [Writing Custom Tasks](https://tomo-deploy.com/tutorials/writing-custom-tasks/) tutorial for an in-depth guide to extending tomo.
175
+ [→ The **Writing Custom Tasks** tutorial has an in-depth explanation of how plugins work.](https://tomo-deploy.com/tutorials/writing-custom-tasks/)<br>
176
+ [→ The **TaskLibrary** API is tomo's DSL for building tasks.](https://tomo-deploy.com/api/TaskLibrary/)<br>
177
+ [→ The **Publishing a Plugin** tutorial explains how to package your plugin as a Ruby gem to share it with the community.](https://tomo-deploy.com/tutorials/publishing-a-plugin/)
160
178
 
161
179
  ## Tutorials
162
180
 
@@ -20,7 +20,7 @@ module Tomo
20
20
  class << self
21
21
  attr_accessor :show_backtrace
22
22
 
23
- def exit(status=true)
23
+ def exit(status=true) # rubocop:disable Style/OptionalBooleanParameter
24
24
  Process.exit(status)
25
25
  end
26
26
  end
@@ -1,6 +1,6 @@
1
1
  class Tomo::CLI::Rules
2
2
  class Argument
3
- def initialize(label, multiple: false, required: false, values_proc:)
3
+ def initialize(label, values_proc:, multiple: false, required: false)
4
4
  @label = label
5
5
  @multiple = multiple
6
6
  @required = required
@@ -15,7 +15,7 @@ class Tomo::CLI::Rules
15
15
  state.parsed_arg(arg)
16
16
  end
17
17
 
18
- def candidates(literal: false, state:)
18
+ def candidates(state:, literal: false)
19
19
  values(state).reject { |val| literal && val.start_with?("-") }
20
20
  end
21
21
 
@@ -1,6 +1,6 @@
1
1
  class Tomo::CLI::Rules
2
2
  class Switch
3
- def initialize(key, *switches, required: false, callback_proc:, &convert_proc)
3
+ def initialize(key, *switches, callback_proc:, required: false, &convert_proc)
4
4
  @key = key
5
5
  @switches = switches
6
6
  @callback_proc = callback_proc
@@ -28,7 +28,7 @@ class Tomo::CLI::Rules
28
28
  state.parsed_option(key, value)
29
29
  end
30
30
 
31
- def candidates(switch=nil, literal: false, state:)
31
+ def candidates(switch=nil, state:, literal: false)
32
32
  return [] if literal
33
33
 
34
34
  vals = values(state)
@@ -1,4 +1,5 @@
1
1
  require "erb"
2
+ require "fileutils"
2
3
 
3
4
  module Tomo
4
5
  module Commands
@@ -99,6 +100,10 @@ module Tomo
99
100
  Gem::Requirement.new(">= 2.2").satisfied_by?(erb_version)
100
101
  end
101
102
 
103
+ def ruby_version_file?
104
+ File.exist?(".ruby-version")
105
+ end
106
+
102
107
  def config_rb_template(app)
103
108
  path = File.expand_path("../templates/config.rb.erb", __dir__)
104
109
  template = IO.read(path)
@@ -90,7 +90,7 @@ module Tomo
90
90
  plugins_registry = PluginsRegistry.new
91
91
 
92
92
  (["core"] + plugins.uniq).each do |plug|
93
- if %w[. /].include?(plug[0])
93
+ if plug.start_with?(".", "/")
94
94
  plug = File.expand_path(plug, File.dirname(path)) unless path.nil?
95
95
  plugins_registry.load_plugin_from_path(plug)
96
96
  else
@@ -50,7 +50,7 @@ module Tomo
50
50
  HINT
51
51
  end
52
52
 
53
- def highlighted_lines
53
+ def highlighted_lines # rubocop:disable Metrics/AbcSize
54
54
  first = [1, error_line_no - 1].max
55
55
  last = [dsl_lines.length, error_line_no + 1].min
56
56
  width = last.to_s.length
@@ -55,7 +55,7 @@ module Tomo
55
55
 
56
56
  def scan_for_plugins
57
57
  Gem.find_latest_files("#{PLUGIN_PREFIX}/*.rb").map do |file|
58
- file[%r{#{PLUGIN_PREFIX}/(.+).rb$}, 1].tr("/", "-")
58
+ file[%r{#{PLUGIN_PREFIX}/(.+).rb$}o, 1].tr("/", "-")
59
59
  end.uniq.sort
60
60
  end
61
61
  end
@@ -20,7 +20,7 @@ module Tomo
20
20
  path(:"#{method}_path")
21
21
  end
22
22
 
23
- def respond_to_missing?(method, include_private=false)
23
+ def respond_to_missing?(method, include_private)
24
24
  setting?(method) || super
25
25
  end
26
26
 
@@ -11,7 +11,7 @@ module Tomo::Plugin
11
11
  defaults bundler_config_path: ".bundle/config",
12
12
  bundler_deployment: true,
13
13
  bundler_gemfile: nil,
14
- bundler_jobs: "4",
14
+ bundler_jobs: nil,
15
15
  bundler_path: "%{shared_path}/bundle",
16
16
  bundler_retry: "3",
17
17
  bundler_version: nil,
@@ -21,7 +21,7 @@ module Tomo::Plugin
21
21
  releases_path: "%{deploy_to}/releases",
22
22
  revision_log_path: "%{deploy_to}/revisions.log",
23
23
  shared_path: "%{deploy_to}/shared",
24
- tmp_path: "/tmp/tomo",
24
+ tmp_path: "/tmp/tomo-#{SecureRandom.alphanumeric(8)}",
25
25
  tomo_config_file_path: nil, # determined at runtime
26
26
  run_args: [] # determined at runtime
27
27
  )
@@ -12,7 +12,7 @@ module Tomo::Plugin::Core
12
12
  result.success?
13
13
  end
14
14
 
15
- def write(text: nil, template: nil, to:, append: false, **run_opts)
15
+ def write(to:, text: nil, template: nil, append: false, **run_opts)
16
16
  assert_text_or_template_required!(text, template)
17
17
  text = merge_template(template) unless template.nil?
18
18
  message = "Writing #{text.bytesize} bytes to #{to}"
@@ -41,7 +41,7 @@ module Tomo::Plugin::Core
41
41
  current = read_current_release
42
42
 
43
43
  remote.chdir(paths.releases) do
44
- releases = remote.list_files.grep(/^#{RELEASE_REGEXP}$/).sort
44
+ releases = remote.list_files.grep(/^#{RELEASE_REGEXP}$/o).sort
45
45
  desired_count -= 1 if releases.delete(current)
46
46
  return if releases.length <= desired_count
47
47
 
@@ -122,7 +122,7 @@ module Tomo::Plugin::Core
122
122
  result = remote.run("readlink", paths.current, raise_on_error: false, silent: true)
123
123
  return nil if result.failure?
124
124
 
125
- result.stdout.strip[%r{/(#{RELEASE_REGEXP})$}, 1]
125
+ result.stdout.strip[%r{/(#{RELEASE_REGEXP})$}o, 1]
126
126
  end
127
127
  end
128
128
  end
@@ -31,8 +31,7 @@ module Tomo::Plugin::Rbenv
31
31
  end
32
32
 
33
33
  def compile_ruby
34
- require_setting :rbenv_ruby_version
35
- ruby_version = settings[:rbenv_ruby_version]
34
+ ruby_version = version_setting || extract_ruby_ver_from_version_file
36
35
 
37
36
  unless ruby_installed?(ruby_version)
38
37
  logger.info(
@@ -51,5 +50,22 @@ module Tomo::Plugin::Rbenv
51
50
  end
52
51
  false
53
52
  end
53
+
54
+ def version_setting
55
+ settings[:rbenv_ruby_version]
56
+ end
57
+
58
+ def extract_ruby_ver_from_version_file
59
+ path = paths.release.join(".ruby-version")
60
+ version = remote.capture("cat", path, raise_on_error: false).strip
61
+ return version unless version.empty?
62
+
63
+ return RUBY_VERSION if dry_run?
64
+
65
+ die <<~REASON
66
+ Could not guess ruby version from .ruby-version file.
67
+ Use the :rbenv_ruby_version setting to specify the version of ruby to install.
68
+ REASON
69
+ end
54
70
  end
55
71
  end
@@ -7,7 +7,7 @@ module Tomo
7
7
  @concurrency = concurrency
8
8
  end
9
9
 
10
- def to_s # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
10
+ def to_s # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
11
11
  desc = []
12
12
  threads = [applicable_hosts.length, concurrency].min
13
13
  desc << "CONCURRENTLY (#{threads} THREADS):" if threads > 1
@@ -82,7 +82,7 @@ module Tomo
82
82
  end
83
83
 
84
84
  def export_env
85
- exports = @env.reject { |_, value| value.nil? }
85
+ exports = @env.compact
86
86
  return if exports.empty?
87
87
 
88
88
  [
@@ -26,7 +26,7 @@ module Tomo
26
26
  end
27
27
 
28
28
  def ssh_subprocess(script, verbose: false)
29
- ssh_args = build_args(script, verbose)
29
+ ssh_args = build_args(script, verbose: verbose)
30
30
  handle_data = ->(data) { logger.script_output(script, data) }
31
31
 
32
32
  logger.script_start(script)
@@ -50,7 +50,7 @@ module Tomo
50
50
  Tomo.logger
51
51
  end
52
52
 
53
- def build_args(script, verbose=false)
53
+ def build_args(script, verbose: false)
54
54
  options.build_args(host, script, control_path, verbose)
55
55
  end
56
56
 
@@ -15,10 +15,10 @@ module Tomo
15
15
 
16
16
  def assert_valid_executable!
17
17
  result = begin
18
- ChildProcess.execute(executable, "-V")
19
- rescue StandardError => e
20
- handle_bad_executable(e)
21
- end
18
+ ChildProcess.execute(executable, "-V")
19
+ rescue StandardError => e
20
+ handle_bad_executable(e)
21
+ end
22
22
 
23
23
  Tomo.logger.debug(result.output)
24
24
  return if result.success? && supported?(result.output)
@@ -14,7 +14,9 @@ host "user@hostname.or.ip.address"
14
14
 
15
15
  set application: <%= app.inspect %>
16
16
  set deploy_to: "/var/www/%{application}"
17
+ <% unless ruby_version_file? -%>
17
18
  set rbenv_ruby_version: <%= RUBY_VERSION.inspect %>
19
+ <% end -%>
18
20
  set nodenv_node_version: <%= node_version&.inspect || "nil # FIXME" %>
19
21
  set nodenv_yarn_version: <%= yarn_version.inspect %>
20
22
  set git_url: <%= git_origin_url&.inspect || "nil # FIXME" %>
@@ -1,7 +1,7 @@
1
1
  module Tomo
2
2
  module Testing
3
3
  module CLIExtensions
4
- def exit(status=true)
4
+ def exit(status=true) # rubocop:disable Style/OptionalBooleanParameter
5
5
  raise MockedExitError, status
6
6
  end
7
7
  end
@@ -80,9 +80,9 @@ module Tomo
80
80
  end
81
81
 
82
82
  def build_image
83
- Local.capture(
84
- "docker build #{build_dir}"
85
- )[/Successfully built (\S+)$/i, 1]
83
+ tag = "tomo_testing:latest"
84
+ Local.capture("docker build --tag #{tag} #{build_dir}")
85
+ tag
86
86
  end
87
87
 
88
88
  def start_container
@@ -1,3 +1,3 @@
1
1
  module Tomo
2
- VERSION = "1.3.0".freeze
2
+ VERSION = "1.6.0".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tomo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Brictson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-08 00:00:00.000000000 Z
11
+ date: 2021-01-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Tomo is a feature-rich deployment tool that contains everything you need
14
14
  to deploy a basic Rails app out of the box. It has an opinionated, production-tested
@@ -65,7 +65,6 @@ files:
65
65
  - lib/tomo/configuration/environment.rb
66
66
  - lib/tomo/configuration/glob.rb
67
67
  - lib/tomo/configuration/plugin_file_not_found_error.rb
68
- - lib/tomo/configuration/plugin_resolver.rb
69
68
  - lib/tomo/configuration/plugins_registry.rb
70
69
  - lib/tomo/configuration/plugins_registry/file_resolver.rb
71
70
  - lib/tomo/configuration/plugins_registry/gem_resolver.rb
@@ -189,7 +188,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
189
188
  - !ruby/object:Gem::Version
190
189
  version: '0'
191
190
  requirements: []
192
- rubygems_version: 3.1.4
191
+ rubygems_version: 3.2.3
193
192
  signing_key:
194
193
  specification_version: 4
195
194
  summary: A friendly CLI for deploying Rails apps ✨
@@ -1,63 +0,0 @@
1
- module Tomo
2
- class Configuration
3
- class PluginResolver
4
- PLUGIN_PREFIX = "tomo/plugin".freeze
5
- private_constant :PLUGIN_PREFIX
6
-
7
- def self.resolve(name)
8
- new(name).plugin_module
9
- end
10
-
11
- def initialize(name)
12
- @name = name
13
- end
14
-
15
- def plugin_module
16
- plugin_path = [PLUGIN_PREFIX, name.tr("-", "/")].join("/")
17
- require plugin_path
18
-
19
- plugin = constantize(plugin_path)
20
- assert_compatible_api(plugin)
21
-
22
- plugin
23
- rescue LoadError => e
24
- raise unless e.message.match?(/\s#{Regexp.quote(plugin_path)}$/)
25
-
26
- raise_unknown_plugin_error(e)
27
- end
28
-
29
- private
30
-
31
- attr_reader :name
32
-
33
- def assert_compatible_api(plugin)
34
- return if plugin.is_a?(::Tomo::PluginDSL)
35
-
36
- raise "#{plugin} does not extend Tomo::PluginDSL"
37
- end
38
-
39
- def constantize(path)
40
- parts = path.split("/")
41
- parts.reduce(Object) do |parent, part|
42
- child = part.gsub(/^[a-z]|_[a-z]/) { |str| str[-1].upcase }
43
- parent.const_get(child, false)
44
- end
45
- end
46
-
47
- def raise_unknown_plugin_error(error)
48
- UnknownPluginError.raise_with(
49
- error.message,
50
- name: name,
51
- gem_name: "#{PLUGIN_PREFIX}/#{name}".tr("/", "-"),
52
- known_plugins: scan_for_plugins
53
- )
54
- end
55
-
56
- def scan_for_plugins
57
- Gem.find_latest_files("#{PLUGIN_PREFIX}/*.rb").map do |file|
58
- file[%r{#{PLUGIN_PREFIX}/(.+).rb$}, 1].tr("/", "-")
59
- end.uniq.sort
60
- end
61
- end
62
- end
63
- end