Tuesday, October 30, 2012

Using and Abusing Dart Pub

‹prev | My Chain | next›

My Dart library, Hipster MVC, was accepted into Dart Pub today as an official package. To celebrate, I am going to try to use it, in Dart Comics, the original application from which it was extracted.

After reviewing the options on the pub command, it seems that I want to pub install in my web site's public/scripts directory. For that I need a pubspec.yaml file that includes hipster_mvc as a dependency:
name: scripts
  hipster_mvc: any
The pub install command will not work without a name in there. Since this is just the scripts directory, I use the name "scripts". This seems like the kind of thing that could be optional in an application directory. The "any" value in the pubspec indicates that I am OK with any version (i.e. the latest version) of hipster_mvc.

Anyhow, pub install works now, though it berates me a bit:
➜  scripts git:(M1) ✗ ~/local/dart/dart-sdk/bin/pub install
Resolving dependencies...
Downloading hipster_mvc 0.1.0 from hosted...
Warning: Package "scripts" is using a deprecated layout.
See http://www.dartlang.org/docs/pub-package-manager/package-layout.html for details.
Dependencies installed!
I will worry about the warning later. For now, I note that pub install left behind a lock file containing the current version of hipster_mvc and any associated dependencies:
➜  scripts git:(M1) ✗ cat pubspec.lock 
I am used to this approach with bundler from the Ruby world. Since this is an application, I will check this lock file into Git so that anyone else working with the code will be running the same versions of hipster_mvc that I am using.

I also note that the package is not actually installed in public/scripts/packages. Rather, pub install has created a symlink to the cache directory:
➜  scripts git:(M1) ✗ tree packages 
└── hipster_mvc -> /home/chris/.pub-cache/hosted/pub.dartlang.org/hipster_mvc-0.1.0/lib

1 directory, 0 files
Presumably this is so that I do not have to re-download my huge hipster_mvc library again.

With that, I fire up my Dart Comics app in Dartium and find... the following errors in the Console:
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:3000/scripts/packages/hipster_mvc/lib/hipster_sync.dart
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:3000/scripts/packages/hipster_mvc/lib/hipster_view.dart
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:3000/scripts/packages/hipster_mvc/lib/hipster_collection.dart
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:3000/scripts/packages/hipster_mvc/lib/hipster_model.dart
I added the lib in that path to conform to the pub standards. And indeed that path is in my hipster_mvc repository:
➜  hipster-mvc git:(master) tree -I docs
├── lib
│   ├── hipster_collection.dart
│   ├── hipster_history.dart
│   ├── hipster_model.dart
│   ├── hipster_router.dart
│   ├── hipster_sync.dart
│   └── hipster_view.dart
├── pubspec.yaml
└── README.asciidoc

1 directory, 8 files
But it is not in the package pulled from Dart Pub:
➜  scripts git:(M1) ✗ tree packages/hipster_mvc 
├── hipster_collection.dart
├── hipster_history.dart
├── hipster_model.dart
├── hipster_router.dart
├── hipster_sync.dart
└── hipster_view.dart

0 directories, 6 files
From a library developer's perspective, I find the difference confusing. From an application developer's perspective, I appreciate the lack of the extra lib in the path. All of my import statements then become:
// ...


main() {
  HipsterSync.sync = localSync;
  // ...
// ...
With that, I again have my Dart Comics application running. Now with hipster_mvc installed from Dart Pub:

A consequence of the lib vs. no-lib difference is that I cannot simply swap out my pub package for my local repository:
➜  scripts git:(M1) ✗ cd packages 
➜  packages git:(M1) ✗ mv hipster_mvc hipster_mvc.pub
➜  packages git:(M1) ✗ ln -s ~/repos/hipster-mvc
If I reload the application, I again get 404s for my library—this time because they all reside under the lib path:
GET http://localhost:3000/scripts/packages/hipster_mvc/hipster_sync.dart 404 (Not Found) localhost:20
GET http://localhost:3000/scripts/packages/hipster_mvc/hipster_collection.dart 404 (Not Found) localhost:20
GET http://localhost:3000/scripts/packages/hipster_mvc/hipster_view.dart 404 (Not Found) localhost:20
GET http://localhost:3000/scripts/packages/hipster_mvc/hipster_model.dart 404 (Not Found) localhost:20
I could symlink the lib directory from the local repository of hipster_mvc:
➜  packages git:(M1) ✗ rm hipster-mvc 
➜  packages git:(M1) ✗ ln -s ~/repos/hipster-mvc/lib hipster_mvc
Now, if I reload, the application works again and I can try out changes in hipster_mvc before committing them to GitHub.

Pub does support git sources. So I can try the following instead of pulling down the official hipster_mvc package:
name: scripts
    git: git://github.com/eee-c/hipster-mvc.git
If I run pub install with that pubspec.yaml, I find that pub has made a copy of the git repository and symlinked to its lib directory:
➜  scripts git:(M1) ✗ ls -l packages                                                                        
total 4
lrwxrwxrwx 1 chris chris 83 Oct 30 23:31 hipster_mvc -> /home/chris/.pub-cache/git/hipster_mvc-e0a4aab8d6fe782f81c2c9abc83906d5fb92e190/lib
So it seems that I was not too far off base manually symlinking to my local repository's lib directory. And, if I manually symlink like that, I do not have to alter the pubspec.yaml or the pubspec.lock and potentially impact other contributors.

All things considered, Dart Pub is nothing short of amazing. It is not a revolutionary package manager as it is very familiar. But it does have the potential to revolutionize client-side development. Easy installs, a formal mechanism to ensure that everyone has the same package versions without having to check any package code into source code, dependency resolution, easy development of local copies—all make for a very exciting time to be a web developer.

Day #560

No comments:

Post a Comment