# Check whether a required file / shard exists on Compilation time?

**URL:** <https://forum.crystal-lang.org/t/check-whether-a-required-file-shard-exists-on-compilation-time/311>\
**Category:** Help & Support\
**Created:** [January 19, 2019, 4:59am UTC](https://forum.crystal-lang.org/t/check-whether-a-required-file-shard-exists-on-compilation-time/311 "2019-01-19T04:59:26Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![anykeyh](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/anykeyh/32/726_2.png) [@anykeyh](https://forum.crystal-lang.org/u/anykeyh)\
**Post date:** [January 19, 2019, 4:59am UTC](https://forum.crystal-lang.org/t/check-whether-a-required-file-shard-exists-on-compilation-time/311/1 "2019-01-19T04:59:26Z")

</div>

I was wondering if there’s possibility to sort of repeat this Ruby behavior but in crystal (and at compile time):

```ruby
begin
  require "some_gem"
  require "./ext_for_some_gem" #< Require my code only if some_gem is available
rescue LoadError => e
  #Do not add my flavored code as the optional gem is not available
end

```

Which may look in crystal:

```crystal
{% if require_exists?("some_shard") %}
   require "some_shard"
   require "./ext_for_some_shard"
{% end %}

```

This will allow also to create more beautiful compiler error code like this:

```crystal
require "a_shard/ext/kemal"
# ^~~~ ERROR: You required ext/kemal but kemal wasn't found in your 
# shards.yml ! Please install kemal first by adding:
# -----
# kemal:
# github: kemalcr/kemal
# -----
# in your shards.yml 

```

I don’t know if anything like this exists already in the macro system. Any thought about it?

---

<div class="post-metadata">

**Author:** ![vladfaust](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/vladfaust/32/48_2.png) [@vladfaust](https://forum.crystal-lang.org/u/vladfaust)\
**Post date:** [January 19, 2019, 12:55pm UTC](https://forum.crystal-lang.org/t/check-whether-a-required-file-shard-exists-on-compilation-time/311/2 "2019-01-19T12:55:14Z")

</div>

It was discussed on Gitter. In 27.0.1 there will be `read_file` macro method which would raise on file missing – [#6967](https://github.com/crystal-lang/crystal/pull/6967) and [#7094](https://github.com/crystal-lang/crystal/pull/7094). As shards usually being stored in `my_project/lib`, you could check a shard’s existence like `read_file?("lib/my_shard")`. However, IIRC @ysbaddaden once mentioned that this would not work if shards aren’t in the `lib` folder. Thus I’d be happy if we had explicit `require? "shard"` method.

---

<div class="post-metadata">

**Author:** ![RX14](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/rx14/32/633_2.png) [@RX14](https://forum.crystal-lang.org/u/RX14)\
**Post date:** [January 26, 2019, 9:53pm UTC](https://forum.crystal-lang.org/t/check-whether-a-required-file-shard-exists-on-compilation-time/311/3 "2019-01-26T21:53:01Z")

</div>

What’s the usecase? There’s no concept of optional import in most languages, so it doesn’t seem so necessary to me.

---

<div class="post-metadata">

**Author:** ![anykeyh](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/anykeyh/32/726_2.png) [@anykeyh](https://forum.crystal-lang.org/u/anykeyh)\
**Post date:** [January 27, 2019, 3:28am UTC](https://forum.crystal-lang.org/t/check-whether-a-required-file-shard-exists-on-compilation-time/311/4 "2019-01-27T03:28:24Z")

</div>

> What’s the usecase? There’s no concept of optional import in most languages, so it doesn’t seem so necessary to me.

Well I’m not agreeing about it. This concept is quite used in Ruby, but not only in Ruby. In some languages, there’s concept of optional import. Just yesterday, toying with [dwm](https://git.suckless.org/dwm/file/dwm.c.html#l39):

```cpp
     #ifdef XINERAMA
     #include <X11/extensions/Xinerama.h>
     #endif /* XINERAMA */

```

In Ruby, it’s [very common pattern](https://stackoverflow.com/questions/2061528/how-to-rescue-from-a-require-gem-name-when-the-gem-is-not-installed)

In cpp it uses flags to add or remove features on build. I’m just thinking this is obsolete with a language like Crystal. Since the shards are present in a relative `lib` directory, we can assume than if we install a specific shard in our project, it’s for using it.

## Example

Let’s assume I’ve a shard to upload in a bucket some files. I offer interface for AWS, Google Cloud Storage, and Digital Ocean.

For each of these gateway I’ve a dependent shard I use to connect to theses API.

Currently:

- When someone is installing my shard, it will install all the dependent shards, for AWS, Google Cloud etc…
- The source-code will bloat, while the final user want only one environnement, not all the providers.

_OR_

- Explain to the final user how to include the dependency for the environment he wants
- Ask the final user to require both my shard (`require "myshard"`) and the file connecting to gateway he wants (`require "myshard/aws"`)

Now with this feature, the process for the final user would be:

- If the user want to use a specific gateway without the shard installed, will drop a compile-time raise error which describe what to do.

---

<div class="post-metadata">

**Author:** ![RX14](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/rx14/32/633_2.png) [@RX14](https://forum.crystal-lang.org/u/RX14)\
**Post date:** [January 27, 2019, 1:44pm UTC](https://forum.crystal-lang.org/t/check-whether-a-required-file-shard-exists-on-compilation-time/311/5 "2019-01-27T13:44:45Z")

</div>

I much prefer the `require "myshard/extension"` because it’s more explicit.

---

<div class="post-metadata">

**Author:** ![vladfaust](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/vladfaust/32/48_2.png) [@vladfaust](https://forum.crystal-lang.org/u/vladfaust)\
**Post date:** [January 27, 2019, 8:22pm UTC](https://forum.crystal-lang.org/t/check-whether-a-required-file-shard-exists-on-compilation-time/311/6 "2019-01-27T20:22:43Z")

</div>

I just want to note that the `"myshard/extension"` feature has been discussed before in [Impossible to require "extension" shards · Issue #172 · crystal-lang/shards · GitHub](https://github.com/crystal-lang/shards/issues/172)

> I’m sorry, but we won’t implement this feature. An incompatible solution was chosen long ago, and we don’t plan to change it.

---

<div class="post-metadata">

**Author:** ![RX14](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/rx14/32/633_2.png) [@RX14](https://forum.crystal-lang.org/u/RX14)\
**Post date:** [January 28, 2019, 12:22pm UTC](https://forum.crystal-lang.org/t/check-whether-a-required-file-shard-exists-on-compilation-time/311/7 "2019-01-28T12:22:27Z")

</div>

@vladfaust this is about extensions contained _within_ `myshard`, so that issue isn’t relevant.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [January 28, 2019, 2:07pm UTC](https://forum.crystal-lang.org/t/check-whether-a-required-file-shard-exists-on-compilation-time/311/8 "2019-01-28T14:07:04Z")

</div>

I always thought this was a really bad design or pattern in Ruby. Then you end up with a general purpose gem that does X but for some reason there’s `if defined?(Rails)` inside it.

It’s probably better to have a base shard `x`, and then another shard `x-rails` that you would require if using Rails.

That said, this works:

```auto
class Foo
end

p {{ @type.constants.any? &.==("Foo") }}

```

So you _could_ use that… but I don’t recommend it.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [December 2, 2019, 10:02pm UTC](https://forum.crystal-lang.org/t/check-whether-a-required-file-shard-exists-on-compilation-time/311/9 "2019-12-02T22:02:03Z")

</div>

I been debating how I want to handle optional dependencies, and this kinda relates to it. I wanted to get some other’s thoughts on how to go about it.

## Use Case

The current plan for my Athena shard is that `athena` itself will be made from some required components (like DI/Routing), but also some optional shards (like logging or validation) that are independent and can be used outside of `athena` itself.

## Problem

This setup works just fine with the required shards as I can explicitly add some `require` within code that integrates that shard into `athena` core; such as registering services for DI, etc. However, things get a bit more tricky when a shard is not required.

An example would be, say someone installs `athena-logger` shard. The goal would be by just installing the shard, `athena` core sets some stuff up in order to integrate the logging component.

### Option 1

Have an ext file within the optional shard that should be required by the user in addition to the shard itself

```auto
require "athena-logger"
require "athena-logger/ext/athena"

```

Not a big fan of this approach since this would make the optional shard depend on additional shards that `athena` core requires, mainly DI.

### Option 2

Have an ext file within `athena` core that should be required by the user if they install the optional shard

```auto
require "athena"
require "athena/ext/logger"

```

This is probably the better more explicit approach

### Option 3

Have some file in `athena` core that auto does the integration if it sees the optional shard is installed

```auto
{% if @type.has_constant? "Athena::Logger::Version" %}
  # Do the setup
{% end %}

```

This would be the most seamless approach as the fact that they installed the other shard says they want to use it within `athena` core. This could also be used in conjunction with option 2.

I think having a better way to handle option 3 would be a nice to have.

---

<div class="post-metadata">

**Author:** ![rogerdpack](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/rogerdpack/32/117_2.png) [@rogerdpack](https://forum.crystal-lang.org/u/rogerdpack)\
**Post date:** [December 3, 2019, 4:56am UTC](https://forum.crystal-lang.org/t/check-whether-a-required-file-shard-exists-on-compilation-time/311/10 "2019-12-03T04:56:56Z")

</div>

There’s read\_file?  
not sure if that’s enough :) [https://crystal-lang.org/api/0.31.1/Crystal/Macros.html](https://crystal-lang.org/api/0.31.1/Crystal/Macros.html)
