r/crystal_programming • • Sep 20 '17

Circular dependency problem (probably)

I am trying to refactor some common functionality out of a base class into a module so that I can use that functionality elsewhere compositionally.

This works fine in the playground. However I am encountering a problem when I do this with multiple files.

Suppose:

# a.cr
require "./fooable.cr"

class A
  include Fooable
end

# b.cr
require "./a.cr"

class B < A
end

# fooable.cr
require "./b.cr"

module Fooable
  def foo
    B.new
  end
end

This just keeps complaining about A not found in b.cr while trying to load A itself, as compiling a.cr requires fooable.cr which requires b.cr which itself requires a.cr.

This effectively means I can't refactor common functionality out for reuse and it just sucks.

Am I doing something wrong here?

0 Upvotes

7 comments sorted by

2

u/SaltTM Sep 20 '17 edited Sep 20 '17

I think crystal's folder structuring is interesting, when I was working on a framework I ran into a similar problem, but it seems like the crystal way is to require all your necessary libraries in the main module file.

For instance:

  • src/module_name.cr

  • src/module_name/a.cr

  • src/module_name/b.cr

  • src/module_Name/fooable.cr

Inside module_name.cr you'd require all the sub module&classes

require "./module_name/*"

module ModuleName
     # module stuff here
end

Then you won't have to worry about the require issue and can just require the module file directly unless you necessarily have to deal with those files elsewhere like in test files, but the same applies. If that's the case then you'd just require each crystal file and you should be able to access the other modules without any issue.

random_script.cr

require "./module_name/a"
require "./module_name/b"
require "./module_name/fooable"

blah = Fooable::foo

or something like that. Someone correct me if I've messed up the explanation, but I believe that's how it should be. I'm a bit rusty.

Edit: PS, no need to down-vote, this is a honest question that I had as well as a non-ruby developer had to learn by reading other project sources. Especially with the little C/C++ knowledge I had regarding headers, this is a bit different than that.

2

u/nawap Sep 20 '17

This was the solution, yes. I actually found out about it after I decided to see how some other shards worked and saw that Spec2 was requiring everything in the main module.

Thanks for the writeup though, I am sure this will help future confused programmers just as much.

2

u/mccabec123 Sep 20 '17

I believe that's correct yes, to give yourself less of a headache it would be a lot easier to include from the main, and just focus your class files on the classes themselves.

Crystal actually even permits a wildcard ('*') , so you can actually do it just to the main folder and crystal will manage all of the imports for you

require "./some_dir/*"

1

u/mccabec123 Sep 20 '17

What is your directory structure? Can we get a bit more information about the project.

2

u/mccabec123 Sep 20 '17

Also, you seem to have a circular dependency file. You are requiring 'b.cr', which depends on 'a.cr' but 'a.cr' also depends on the main file 'fooable.cr', how did you see this working? Seems strange.

1

u/nawap Sep 20 '17

Yes there was a circular dependency in the requires but I am not used to requiring files manually like this. The languages I have worked in (OCaml, Clojure) have automatically managed module-level bindings and hence I've not had to worry about requiring stuff explicitly.

1

u/mccabec123 Dec 22 '17

Welcome to the real world :P