cargo
cargo copied to clipboard
Dependency analysis
Thanks to Cargo, it is possible to establish which modules depend on which other modules (export can remember which imports happened in that same file), collect the information, and then expose it somehow.
A way to expose it would be to graph it out, a bit like:
https://github.com/hilverd/lein-ns-dep-graph
Do you reckon this belongs into Cargo, or as a separate gem that would hook into import/export? (how would it hook then, maybe Cargo should accept import/export hooks?)
I think this probably doesn't belong to Cargo, but to a package manager. I think Cargo's import is a replacement for Ruby's require, and there's still the need for something to replace RubyGems. I have some ideas for a package manager, a few of them expressed in this tool: https://github.com/soveran/pac
But that tool is very limited as the use case is very uncommon (even though it's a real use case, and in that scenario the tool works great). The idea I like is the use of a hashing algorithm for identifying the file, instead of relying on versions. That idea comes from the fact that versions are generated by humans, and usually contain errors, while a shasum
of a file is a good guarantee for making sure only the intended file can be loaded. But as I said, that tool is very limited in scope.
I like the idea of having a package manager that allows the addition of plugins, and one of those plugins could be a dependency graph generator.
Being the owner of an app using 200 gems in my Gemfile, I feel that package management is a much bigger problem for Ruby than most Rubyists are recognizing.
Rather than replacing Rubygems full-stop, how about making a Cargo-compliant package manager which can interoperate with RubyGems and Bundler? RubyGems could be used purely as a delivery mechanism, i.e. treat it as a source from which to download code, equivalent to fetching from Git or elsewhere. The package manager should be able to load "fully Cargo-ized" gems using a dependency graph (both the gem AND all of it's dependencies would need to be Cargo-ized), and at the same time load gems from Bundler into the global scope (a.k.a. the garbage dump!)
The Cargo-specific package manager would not have to care of things like version conflicts or from where it received gems (Rubygems, Github, Git, path) because it can load n different versions of the same gem. (Ideally you'd want to minimize n, but a proof-of-concept implementation wouldn't require it.)
The biggest problem for adoption would be how non-Cargo gems can become Cargo-ized. If I have to beg each of the owners of the 200 gems I use to replace require
with import/export
in their repo, clearly that's not going to happen. If however there were an easy way to "wrap" existing gems with a Cargo wrapper and clear migration path, I can see this actually being adopted.
Worth noting the same migration has happened in the Javascript community with the introduction of:
- ECMAscript 6 modules (= Cargo)
- NPM (= the "package manager")
- Babel JS, Browserify (= the means to "wrap" legacy non-Cargo code)
CC @bbozo
@johnnyshields i'm very interested in the possibility you're sketching out. i would think one could write a fairly simple wrapper that overrides require and reroutes it to cargo calls.
@mooreniemi glad you found this.
require
loads variables into the global scope. import
pulls whatever is being export
ed into the local scope of the file which is import
ing.
If require
behaved like import
, you'd have to require
each lib you need in each file that needs whatever is being required. For example, doing something like this: https://github.com/radar/by_star/blob/master/lib/by_star.rb wouldn't work. So it's not as simple as wrapping require
; it requires a structural change where you move from a "global scope dumping-ground" to "declaring your imports when you need them".
The best way forward IMHO is to be able to use Cargo in parallel with require
/ Bundler / etc and gradually migrate code over as it becomes Cargo-ized.
ah yes of course, i see what you mean.
what if the global dump require
does can be penned in, and from there, exports done? for the basic use case where someone doesnt want to know about import/export, then you can just export all to the global namespace as default (a pass thru). then as someone wants to exclude, they can at least use the "except" or "as" functionality to begin with
meanwhile someone willing to learn how to fully utilize the system can import per file as normal
Any attempt to override require
is asking for trouble. In the best possible scenario you may end up with several global dumps, but then you have the problem of how to share variables/constants between the dumps (== lots of code changes.) I don't see what problem this solves, and anyway it's not true isolation at the file-level like import / export
provides.
It is perfectly fine to have some libs use require
(global dump) and others use import / export
(isolation) in the same project. This approach would allow a lib-by-lib migration to the new structure.
It's also worth noting that you can make libs dual Cargo and non-Cargo use at the same time. If you add export(Foo) if defined?(export)
at the bottom of the file, you can still do require 'my_lib/foo'
the file which will dump Foo
(and any other constants) to the global dump or MyVar = import('my_lib/foo')
in which case you get an isolated Module(<anonymous>)::Foo
idk about asking for trouble, but it is constraining by strict dependency. that is a major tradeoff. i agree it makes more sense to be interoperable where possible.
as for what problem it solves, i only see it as a way to first change how people do module loading as a incremental step towards the agreed goal of per-file import/export. it's a way of giving a useful feature (removing from namespace, aliasing) by wrapping require. making require an implementation detail of cargo, essentially.
in terms of what is actionable for a parallel usage, what do you see, other than simply using it in a project as you described? (in other words, what do you see the aim as the convo now, other than to further sketch out the implementation you wish to demonstrate?)
I think the aim is to get as many gems / projects using cargo as possible. To do this, we'd need provide a clear upgrade path that:
- doesn't change the behavior of existing code
- introduces new methods (
import / export
) which can be layered onto existing code - works with and extends existing tools like Rubygems, Bundler etc.
Hello @johnnyshields and @mooreniemi, I'm all for trying out these ideas. Over the years, a lot of people have told me about their need for something like this, so it looks like a legitimate use case.
I have a proof of concept for loading cargo-compatible gems, I'll share the code soon :-)
@soveran that's great to hear, really looking forward to seeing it.
@johnnyshields I just pushed the proof of concept to https://github.com/soveran/packer
Wow great start, like Cargo it's only a few lines of code yet very powerful. Let's move the discussion there.