# Dry-cli: How to reuse a base class with predefined options

**URL:** <https://discourse.dry-rb.org/t/dry-cli-how-to-reuse-a-base-class-with-predefined-options/1032>\
**Category:** Support\
**Created:** [April 7, 2020, 10:27am UTC](https://discourse.dry-rb.org/t/dry-cli-how-to-reuse-a-base-class-with-predefined-options/1032 "2020-04-07T10:27:19Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![vpereira](https://avatars.discourse-cdn.com/v4/letter/v/e9c0ed/32.png) [@vpereira](https://discourse.dry-rb.org/u/vpereira)\
**Post date:** [April 7, 2020, 10:27am UTC](https://discourse.dry-rb.org/t/dry-cli-how-to-reuse-a-base-class-with-predefined-options/1032/1 "2020-04-07T10:27:19Z")

</div>

I’m looking for some ideas how to solve the following issue.

I have the following classes

```auto
class A < Dry::CLI::Command
  desc 'my class A'
  option :host, type: :string, default: 'localhost', desc: 'set the host'
end

class B < Dry::CLI::Command
  desc 'my class B'
  option :host, type: :string, default: 'localhost', desc: 'set the host'
end

```

and because they share the same option I tried to refactor to something like:

```auto
class BaseCommand < Dry::Cli::Command
  option :host, type: :string, default: 'localhost', desc: 'set the host'
end

class A < BaseCommand
  desc 'my fancy A class'
end

class B < BaseCommand
  desc 'my fancy B class'
end

```

However calling my application, I see that the option host isn’t recognized for class A and B. There is any trick to do this kind of refactoring?

---

<div class="post-metadata">

**Author:** ![andrewdotnich](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/andrewdotnich/32/51_2.png) [@andrewdotnich](https://discourse.dry-rb.org/u/andrewdotnich)\
**Post date:** [August 16, 2020, 11:55am UTC](https://discourse.dry-rb.org/t/dry-cli-how-to-reuse-a-base-class-with-predefined-options/1032/2 "2020-08-16T11:55:28Z")

</div>

> [@vpereira](#):
>
> There is any trick to do this kind of refactoring?

There is! Instead of using _inheritance_, you can solve this problem with _composition_ - aka “mixins”.  
We want to extract the common options into a module, and `include` that module into each command.

If you want to see how this would work, just skip to the section `Solution`. If you’d like a brief journey on how you might have figured this out yourself, read on 😃

# Attempt #1: Just Put The Code In a Module

If we try this naively, it won’t work:

```ruby
module CommonOptions
  option :host, type: :string, default: 'localhost', desc: 'set the host'
end

class A < Dry::CLI::Command
  ...
  include CommonOptions
end

# .my-cli a --host "8.8.8.8"
# 🔥 undefined method `option' for CommonOptions:Module (NoMethodError)

```

`option` is actually a method call, and the module gets evaluated outside a `Dry::CLI::Command`. so it has no idea what that method should be! We want the module code to be evaluated only when _inside_ the Command object.

# Attempt #2: `eval`? 😬

Our instincts tell us that this might require some kind of `eval`, and our instincts would be right. So we try this:

```ruby
module CommonOptions
  self.instance_eval do
    option :host, type: :string, default: 'localhost', desc: 'set the host'
  end
end
# ...
# 🔥 undefined method `option' for CommonOptions:Module (NoMethodError)

```

but this doesn’t help. (In fact, conceptually, this is almost exactly the same as Attempt 1! Why is that? 🤔 ) Calling `instance_eval` on ourself doesn’t work - it’s too early! We’re still operating within the context of the module, and the module still doesn’t know what `option` means. We want this to be executed later, _in the context of the including Command_. But how would we know which `Command` that is?

# Solution: `Module.included`

Modules in Ruby can define callback methods, which are called whenever certain events happen. One of those is `included`, which is called whenever (you guessed it) its module gets `include`d by something - exactly what we want!

If we define this method for our Options module, we can evaluate that call to `option` inside the Command, where it’s defined:

```ruby
module CommonOptions
  # Called when CommonOptions is included somewhere.
  # mod is the object (a Class, or Module) doing the 
  # including
  def self.included(mod)
    mod.instance_eval do
      option :host, type: :string, default: 'localhost', desc: 'set the host'
    end
  end
end

```

And this works!

```ruby
class A < Dry::CLI::Command
  include CommonOptions

  def call(host:)
    puts "A: #{host}"
  end
end

class B < Dry::CLI::Command
  include CommonOptions

  def call(host:)
    puts "B: #{host}"
  end
end

# my-cli b --host "8.8.8.8"
# ✅ => "B: 8.8.8.8"
# my-cli a
# ✅ => "A: localhost"

```

# PS: Side notes

Some things to ponder: (perhaps! I don’t know what you already know - this is not meant to be at all condescending!)

- What’s the difference between just putting the code in a module (Attempt 1) and wrapping the code in `self.instance_eval` (Attempt 2)? What does this tell you about what `instance_eval` means?
- We noticed that each `option` line is actually a _method call_. But on what object? And if it’s a method, how did it get defined?

---

<div class="post-metadata">

**Author:** ![IvanShamatov](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/ivanshamatov/32/601_2.png) [@IvanShamatov](https://discourse.dry-rb.org/u/IvanShamatov)\
**Post date:** [February 5, 2021, 10:05am UTC](https://discourse.dry-rb.org/t/dry-cli-how-to-reuse-a-base-class-with-predefined-options/1032/3 "2021-02-05T10:05:02Z")

</div>

This functionality is now merged into master, will be in 0.7.0
