rbp 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,194 @@
1
+ rbp: Ruby Package (manager)
2
+ ===========================
3
+
4
+ rbp is a package manager for ruby. It is an alternative to rubygems as
5
+ it installs dependencies locally to your application to make version
6
+ management easier. Packages can also be installed globally, but the
7
+ default is to keep all dependencies tidly inside your app directory.
8
+
9
+ rbp is **not** required at runtime which also removes the additonal
10
+ loading required to make rbp operate. All packages are installed into
11
+ the `vendor/packages` directory inside your local directory.
12
+
13
+ **rbp is still in pre-pre-pre-pre-alpha**
14
+
15
+ Installation
16
+ ------------
17
+
18
+ rbp can be installed directly by the following command. This will just
19
+ download the latest version of rbp and install its `lib` and `bin`
20
+ directories into the siteruby directory for your ruby install. Aslong as
21
+ these are in the loadpath (which they should be), then rbp will just
22
+ work.
23
+
24
+ To install rbp just do:
25
+
26
+ ```
27
+ $ curl -s https://raw.github.com/adambeynon/rbp/master/setup.rb | ruby
28
+ ```
29
+
30
+ If you use rbenv or rvm then you may need to run this command for
31
+ each ruby install. To make sure rbp is working, just run:
32
+
33
+ ```
34
+ $ rbp version
35
+ ```
36
+
37
+ If the command cannot be found, check your shells PATH variable.
38
+
39
+ How it works
40
+ -------------
41
+
42
+ In your app directory there will be a `vendor/packages` directory where
43
+ each dependancy is stored in its own directory by name. For example:
44
+
45
+ ```
46
+ some_app/
47
+ |-bin/
48
+ |-lib/
49
+ |-vendor/
50
+ |-packages/
51
+ |-init.rb
52
+ |-rake/
53
+ |-otest/
54
+ ```
55
+
56
+ Installing or removing (or updating) packages will be done through the
57
+ `rbp` command, which is simply a ruby library. rbp is only needed during
58
+ development - not production. Of course, the packages directory should be
59
+ added to `.gitignore` (or similar).
60
+
61
+ ### init.rb
62
+
63
+ `init.rb` in the packages directory is where all the magic happens. As
64
+ packages are added or removed, their load paths are registered inside
65
+ init.rb automatically by rbp, so when your package code runs, all the
66
+ relevant load paths are setup so you can just require() away. Your
67
+ packages own load path is also added to this file, so simply requiring
68
+ this file will add your library to the load path as well.
69
+
70
+
71
+ ### Why use init.rb?
72
+
73
+ Ruby can only load files from loadpaths that are set on the `$:` array.
74
+ Rubygems works by overriding `require` to perform dynamic lookup for
75
+ libraries. rbp does not have any runtime component, so dependencies must
76
+ be in the loadpath on running. To do this, `init.rb` adds all
77
+ dependency load paths automatically for you. The only requirement is
78
+ that init.rb is run straight away, and this is what the `rbp` command
79
+ does.
80
+
81
+ When deploying an application, `rbp` will not be available - and here
82
+ you have two choices. Considering deployed applications will not be used
83
+ as libraries themselves, you can load init.rb in your app directly. For
84
+ example, in rails you might do this in config.ru:
85
+
86
+ ```ruby
87
+ File.expand_path('../vendor/packages.init.rb', __FILE__)
88
+ ```
89
+
90
+ which will run the init.rb file for you, so all dependencies will be
91
+ ready to run. Also, this allows you to use a vendor version of rails
92
+ itself which may be very specific to your app (2.3 vs 3.0 vs 3.1 etc).
93
+
94
+ When a dependency is not found on the loadpath, a fallback on rubygems
95
+ may be used to use a global gem as the last option (probably will happen
96
+ as a lot of ruby installs will load rubygems automatically).
97
+
98
+ ### Local development
99
+
100
+ rbp comes with a handy command `irb` which will open irb with the init
101
+ file already added to the load path so you can just require your app
102
+ dependencies directly. For example:
103
+
104
+ ```
105
+ $ rbp irb
106
+
107
+ irb> require 'dependency_1'
108
+ ```
109
+
110
+ To run bin files either inside your local package, or an included
111
+ dependency, the `exec` command will setup your load paths as well. Run
112
+ from the command line:
113
+
114
+ ```
115
+ $ rbp exec bin_from_a_dependency arg1 arg2 arg3
116
+ ```
117
+
118
+ To get all dependencies on the load path, init.rb needs to be called.
119
+ rbp does this for you by wrapping common ruby commands, but any
120
+ additional methods might mean having to require init.rb directly. For
121
+ example, a Rakefile:
122
+
123
+ ```ruby
124
+ require File.expand_path('../vendor/packages/init.rb')
125
+ require 'your_lib_name'
126
+ ```
127
+
128
+ When your lib requires its dependencies they will already be on the load
129
+ path thanks to init.rb.
130
+
131
+ **NOTE:** init.rb also adds your actual app/packages load path into the
132
+ ruby load paths as well so you don't have to.
133
+
134
+ ### Listing dependencies and package.yml
135
+
136
+ An apps dependencies are listed in its package.yml file, which is in the
137
+ base directory. It is based off/inspired by commonjs' package.json. rbp
138
+ doesn't use gemspecs because it aims to be a replacement/alternative to
139
+ rubygems.
140
+
141
+ Dependencies are listed as a hash in the package.yml file and it can
142
+ list either their required version numbers or a git url. Packages with
143
+ versions numbers are download directly from rubygems.org server and
144
+ converted from gems into packages.
145
+
146
+ ### Example init.rb
147
+ ----------------
148
+
149
+ **Note:** this is created automatically by rbp - you do not need to
150
+ create this yourself.
151
+
152
+ ```ruby
153
+ # your packages' path
154
+ path = File.expand_path('..', __FILE__)
155
+
156
+ # your packages' lib
157
+ $:.unshift File.expand_path("#{path}/../lib")
158
+
159
+ # dependency 1: `otest'
160
+ $:.unshift File.expand_path("#{path}/../packages/otest/lib")
161
+
162
+ # dependency 2: `rake'
163
+ $:.unshift File.expand_path("#{path}/../packages/rake/lib")
164
+ ```
165
+
166
+ Installing packages globally
167
+ ----------------------------
168
+
169
+ Some packages may want to be installed globally, mainly as a
170
+ compatibility with the way rubygems does things. For example, rails
171
+ generates a template directory for you, so it may not be possible to add
172
+ rails as a dependency before you setup your dir structure. For this
173
+ reason, global packages are supported.
174
+
175
+ ### Using rubygems as a 'legacy' system
176
+
177
+ For the short term, global packages in rbp will just be gem
178
+ installations - rbp will just search for, and install, gems. This will
179
+ allow any package that is not in the local packages directory to be
180
+ loaded by rubygems itself - we could add warnings stating that you are
181
+ using a global package. If you then install a different version locally,
182
+ then that will be used instead, as its lib path is already on the load
183
+ path before rubygems searches for an alternative.
184
+
185
+ In the longer term, the plan is to replace rubygems and global packages
186
+ will be installed in a similar manner to rubygem packages, including
187
+ wrapped bin files which allow this dynamic lib lookup. When local
188
+ packages then need to use globally installed ones, a **very** smal
189
+ runtime component from rvm can be loaded to assist lib lookup.
190
+
191
+ Global packages should still be used for development purposes, and the
192
+ idea will be to copy all required global packages to the local directory
193
+ ready for production.
194
+
data/bin/rbp ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbp'
4
+ require 'rbp/command'
5
+
6
+ Rbp::Command.new ARGV
7
+
@@ -0,0 +1,7 @@
1
+ require 'rbp/package'
2
+
3
+ module Rbp
4
+ VERSION = "0.0.1"
5
+ def self.version; VERSION; end
6
+ end
7
+
@@ -0,0 +1,153 @@
1
+ require 'rbp'
2
+ require 'rbp/package'
3
+ require 'rbp/package_manager'
4
+ require 'rbp/dependency'
5
+ require 'rbp/git_dependency'
6
+ require 'rbp/dependency_installer'
7
+ require 'rbp/dependency_uninstaller'
8
+
9
+ module Rbp
10
+ class Command
11
+ # All valid commands that can be run from command line
12
+ COMMANDS = [:install, :exec, :irb, :ls, :version]
13
+
14
+ # Creates a nw instance of the command utility which will then delegate
15
+ # to sub commands (methods) to handle individual commands.
16
+ #
17
+ # @param [Array<String>] args command line args
18
+ def initialize(args)
19
+ command = args.shift
20
+
21
+ if command and COMMANDS.include?(command.to_sym)
22
+ __send__ command.to_sym, *args
23
+ else
24
+ help
25
+ end
26
+ end
27
+
28
+ def install(*)
29
+ manager = PackageManager.new
30
+ manager.install_dependencies
31
+ end
32
+
33
+ # Takes a Gem dependency and installs it into the local packages/
34
+ # directory.
35
+ #
36
+ # The installed gem/package is then inspected and all of its load
37
+ # paths are registered in init.rb so they are available at runtime.
38
+ #
39
+ # Finally, all of the dependecies of the dependency given will be
40
+ # installed.
41
+
42
+ def install_dependency(dependency, force = false)
43
+ name = dependency.name
44
+ path = package_install_path(name)
45
+
46
+ if File.exists? path
47
+ unless force
48
+ info "Skipping `#{name}'"
49
+ return
50
+ end
51
+
52
+ info "Removing `#{name}'"
53
+ uninstall_dependency name
54
+ end
55
+
56
+ spec = dependency.matching_specs.first
57
+
58
+ if spec and File.exists? spec.full_gem_path
59
+ info "Linking `#{name}'"
60
+ cp_r spec.full_gem_path, path
61
+ else
62
+ spec = install_remote_dependency dependency
63
+ end
64
+
65
+ register_load_paths spec
66
+
67
+ puts "going over dependencies.."
68
+
69
+ spec.runtime_dependencies.each do |dep|
70
+ install_dependency dep
71
+ end
72
+ end
73
+
74
+ ##
75
+ # Takes a Gem dependency and installs it into the local packages/
76
+ # directory. This will **always** go out and retrieve a remote gem
77
+ # matching the dependency, it will never link to a locally installed
78
+ # one.
79
+ #
80
+ # This can be seen as sandboxing a gem installation just to the local
81
+ # package - nothing is done globally.
82
+ #
83
+ # This will then return the spec file for the installed dependency as
84
+ # it is used for registering load paths etc
85
+
86
+ def install_remote_dependency(dependency)
87
+ name = dependency.name
88
+ dest = package_install_path(name)
89
+ info "Installing `#{name}'"
90
+
91
+ spec, uri = begin
92
+ ins = Gem::DependencyInstaller.new
93
+ ins.find_spec_by_name_and_version(name, dependency.requirement)[0]
94
+ rescue Gem::GemNotFoundException => e
95
+ abort "Could not find dependency `#{name}'"
96
+ end
97
+
98
+ gem_path = Gem::RemoteFetcher.fetcher.download(spec, uri, gems_path)
99
+
100
+ installer = Gem::Installer.new(gem_path, :unpack => true)
101
+ installer.unpack dest
102
+ installer.build_extensions
103
+
104
+ # erm, the installer somehow gets a different spec..?
105
+ return installer.spec
106
+ end
107
+
108
+ # Execute a bin file in (local?) packages/
109
+ #
110
+ # Currently hardcoded to load bin file in current packages actual
111
+ # bin directory. Using bins from dependencies not yet supported.
112
+ #
113
+ # Usage:
114
+ #
115
+ # rbp exec bin_name arg1 arg2 arg3
116
+ #
117
+ # This command will also require the init.rb in the packages
118
+ # directory of the local package so all libs, including
119
+ # dependencies, will be available.
120
+ def exec(bin, *arg)
121
+ command = "ruby -r ./vendor/packages/init.rb bin/#{bin} #{arg.join ''}"
122
+ puts " exec `#{command}'\n\n"
123
+ system command
124
+ end
125
+
126
+ # This command will simply open a regular irb session, passing
127
+ # any given arguments.
128
+ #
129
+ # The init.rb file will automatically be required so all
130
+ # installed dependencies, and the local package itself, will be
131
+ # ready on the load path.
132
+ def irb(*args)
133
+ command = "irb -r ./vendor/packages/init.rb #{args.join ' '}"
134
+ system command
135
+ end
136
+
137
+ # List all dependencies of this package
138
+ def ls
139
+ raise "not implemented"
140
+ end
141
+
142
+ # version info
143
+ def version
144
+ puts Rbp.version
145
+ end
146
+
147
+ # Simply print help
148
+ def help
149
+ puts "Usage: rbp <command>"
150
+ end
151
+ end
152
+ end
153
+
@@ -0,0 +1,19 @@
1
+ module Rbp
2
+ class Dependency
3
+ # dependency name, i.e. package name
4
+ attr_reader :name
5
+
6
+ # version required of dependency, e.g. 0.1.0
7
+ attr_reader :version
8
+
9
+ def initialize(name, version = nil)
10
+ @name = name
11
+ @version = version || ">= 0"
12
+ end
13
+
14
+ def inspect
15
+ "<#{self.class} name=#{name.inspect} version=#{version.inspect}>"
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,123 @@
1
+ require 'fileutils'
2
+ require 'tmpdir'
3
+ require 'rubygems/dependency_installer'
4
+
5
+ module Rbp
6
+ class DependencyInstaller
7
+
8
+ attr_reader :package
9
+
10
+ def initialize(dependency)
11
+ @dependency = dependency
12
+ end
13
+
14
+ # Install the dependency into the given environment. The environment
15
+ # has all the paths and files etc needed for installing a dependency
16
+ # into.
17
+ #
18
+ # @param [Environment] environment the environment to install to
19
+ # @return [Package] returns the package for the installed dependency
20
+ def install(package_manager)
21
+ @package_manager = package_manager
22
+
23
+ case @dependency
24
+ when Dependency
25
+ install_dependency @dependency
26
+ when GitDependency
27
+ install_git_dependency @dependency
28
+ else
29
+ raise "Bad dependency to install"
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def install_dependency(dependency)
36
+ gem_dep = Gem::Dependency.new dependency.name, dependency.version
37
+ name = dependency.name
38
+ destination = @package_manager.package_dir_for(name)
39
+
40
+ spec, uri = begin
41
+ ins = Gem::DependencyInstaller.new
42
+ ins.find_spec_by_name_and_version(name, gem_dep.requirement).first
43
+ rescue Gem::GemNotFoundException => e
44
+ raise "Could not find dependency `#{name}'"
45
+ end
46
+
47
+ tmp_path = File.join(Dir.tmpdir, "#{name}_#{Time.now.to_i}")
48
+ FileUtils.mkdir_p tmp_path
49
+
50
+ gem_path = Gem::RemoteFetcher.fetcher.download(spec, uri, tmp_path)
51
+
52
+ installer = Gem::Installer.new(gem_path, :unpack => true)
53
+ installer.unpack destination
54
+ installer.build_extensions
55
+
56
+ FileUtils.rm_rf tmp_path
57
+
58
+ @package = Package.from_gemspec installer.spec
59
+ descriptor_path = @package_manager.descriptor_file_for name
60
+ FileUtils.mkdir_p File.dirname(descriptor_path)
61
+
62
+ File.open(descriptor_path, 'w+') { |o| o.write @package.to_yaml }
63
+
64
+ register_load_paths @package
65
+
66
+ return @package
67
+ end
68
+
69
+ def install_git_dependency(dependency)
70
+ name = dependency.name
71
+ url = dependency.url
72
+ dest = @package_manager.package_dir_for(name)
73
+
74
+ system "git clone --quiet #{url} #{dest}"
75
+
76
+ gemspec = Dir["#{dest}/*.gemspec"].first
77
+ package = File.join dest, "package.yml"
78
+
79
+ if gemspec
80
+ raise "gemspec given"
81
+ elsif File.exists? package
82
+ @package = Package.load_path package
83
+ else
84
+ raise "need to make fake package"
85
+ @package = Package.new
86
+ end
87
+
88
+ register_descriptor @package
89
+
90
+ return @package
91
+ end
92
+
93
+ def register_descriptor(package)
94
+ descriptor_path = @package_manager.descriptor_file_for package.name
95
+ FileUtils.mkdir_p File.dirname(descriptor_path)
96
+
97
+ File.open(descriptor_path, 'w+') { |o| o.write package.to_yaml }
98
+ end
99
+
100
+ # Looks through the package and registers all the required load paths
101
+ # into the environment init.rb file.
102
+ #
103
+ # Currently this will just register the 'lib' directory, but it should
104
+ # really look at the vendor lib paths as well
105
+ #
106
+ # @param [Package] package the package to register
107
+ def register_load_paths(package)
108
+ name = package.name
109
+ paths = ['lib'] + package.vendor_lib_paths
110
+
111
+ code = paths.map do |p|
112
+ path = "packages/#{name}/#{p}"
113
+ "$:.unshift File.expand_path(\"\#{path}/#{path}\")\n"
114
+ end.join
115
+
116
+ out = @package_manager.init_file
117
+ read = File.read out
118
+
119
+ File.open(out, 'w+') { |o| o.write(read + code) }
120
+ end
121
+ end
122
+ end
123
+
@@ -0,0 +1,40 @@
1
+ require 'fileutils'
2
+
3
+ module Rbp
4
+ class DependencyUninstaller
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ end
9
+
10
+ def uninstall(package_manager)
11
+ @package_manager = package_manager
12
+
13
+ unless package_manager.installed? @name
14
+ raise "No dependency to uninstall: `#{@name}'"
15
+ end
16
+
17
+ descriptor_path = package_manager.environment.descriptor_file_for(@name)
18
+ @package = Package.load_path descriptor_path
19
+
20
+ FileUtils.rm_rf package_manager.environment.package_dir_for(@name)
21
+ FileUtils.rm_f descriptor_path
22
+
23
+ unregister_load_paths @package
24
+ end
25
+
26
+ private
27
+
28
+ def unregister_load_paths(package)
29
+ name = package.name
30
+ init = @package_manager.environment.init_file
31
+
32
+ amended = File.read(init).each_line.reject do |line|
33
+ line =~ /packages\/#{name}/
34
+ end.join
35
+
36
+ File.open(init, 'w+') { |o| o.write amended }
37
+ end
38
+ end
39
+ end
40
+
@@ -0,0 +1,18 @@
1
+ module Rbp
2
+ class GitDependency
3
+
4
+ attr_reader :name
5
+
6
+ attr_reader :url
7
+
8
+ def initialize(name, url)
9
+ @name = name
10
+ @url = url
11
+ end
12
+
13
+ def inspect
14
+ "<#{self.class} name=#{@name.inspect} url=#{@url.inspect}>"
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,16 @@
1
+ require 'rubygems/dependency_installer'
2
+
3
+ module Rbp
4
+ class DependencyInstaller
5
+ def self.install(dependency, options = {})
6
+ installer = new
7
+ installer.install(dependency, options)
8
+ installer
9
+ end
10
+
11
+ def install(options)
12
+ name = dependency.name
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,225 @@
1
+ require 'yaml'
2
+
3
+ module Rbp
4
+ # When package not found when loading Package
5
+ class PackageNotFoundError < StandardError; end
6
+
7
+ # A package is a gem
8
+ class Package
9
+
10
+ attr_reader :root
11
+
12
+ ##
13
+ # The package's name.
14
+ #
15
+ # Most remote packages are wrapped as gems, so this name must be a
16
+ # valid rubygem name.
17
+
18
+ attr_accessor :name
19
+
20
+ # The package's version (as a string), e.g. "0.1.0"
21
+ attr_accessor :version
22
+
23
+ # @return [Array<Gem::Dependency>] an array of dependencies
24
+ attr_reader :dependencies
25
+
26
+ # Compatibility with Gem::Specification
27
+ alias_method :runtime_dependencies, :dependencies
28
+
29
+ # @return [Array<Gem::Dependency>] and array of development dependencies
30
+ attr_reader :dev_dependencies
31
+
32
+ # Compatibility with Gem::Specification
33
+ alias_method :development_dependencies, :dev_dependencies
34
+
35
+ # @return [String] path to the directory that this descriptor represents.
36
+ attr_reader :package_dir
37
+
38
+ # @return [Array<String>] array of additional load paths relevant to the
39
+ # package root. This is a way of supporting multiple load paths as offered
40
+ # by rubygems. Rbp doesn't technically support this, so if we pretend to
41
+ # support vendor paths, then this works nicely(ish).
42
+ attr_accessor :vendor_lib_paths
43
+
44
+ # Load a package from the given directory, or the current working
45
+ # directory if no path specified.
46
+ #
47
+ # @param [String] root the root directory to load package.yml from
48
+ # @return [Package] the package that is loaded
49
+ def self.load(root = Dir.getwd)
50
+ p = self.new
51
+ p.load_yaml root
52
+ p
53
+ end
54
+
55
+ # load some/path/to/package.yml (must given package.yml in filename
56
+ # as well)
57
+ def self.load_path(path)
58
+ p = self.new
59
+
60
+ unless File.exists? path
61
+ raise PackageNotFoundError, "Missing package file at `#{path}'"
62
+ end
63
+
64
+ p.load_yaml YAML.load(File.read(path))
65
+
66
+ p.package_dir = File.dirname(path)
67
+
68
+ p
69
+ end
70
+
71
+ # Creates a new Package instance
72
+ def initialize
73
+ @dependencies = []
74
+ @dev_dependencies = []
75
+ end
76
+
77
+ # @param [Hash] yml the yaml object (hash) to load
78
+ def load_yaml(yml)
79
+ @yml = yml
80
+ raise "Bad package.yml format" unless yml
81
+
82
+ @name = @yml['name']
83
+ @version = @yml['version']
84
+
85
+ # dependencies
86
+ if deps = @yml['dependencies']
87
+ raise "Bad dependencies hash in `#{@root}'" unless Hash === deps
88
+
89
+ deps.each do |name, version|
90
+
91
+ d = if version and version =~ /^git\:\/\//
92
+ GitDependency.new name, version
93
+ else
94
+ Dependency.new name, version
95
+ end
96
+
97
+ dependencies << d
98
+ end
99
+ end
100
+
101
+ # dev dependencies
102
+ if deps = @yml['dev_dependencies']
103
+ raise "Bad dev_dependencies hash in `#{@root}'" unless Hash === deps
104
+
105
+ deps.each do |name, version|
106
+
107
+ d = if version and version =~ /^git\:\/\//
108
+ GitDependency.new name, version
109
+ else
110
+ Dependency.new name, version
111
+ end
112
+
113
+ dev_dependencies << d
114
+ end
115
+ end
116
+ end
117
+
118
+ def to_s
119
+ "#<#{self.class} name=\"#{@name}\" version=\"#{@version}\">"
120
+ end
121
+
122
+ # Sets the directory for the package that this package descriptor
123
+ # represents.
124
+ #
125
+ # This may invalidate some other variables such as lib paths, so
126
+ # we reset all those variables here as well.
127
+ #
128
+ # @param [String] path the directory
129
+ def package_dir=(path)
130
+ @package_dir = path
131
+ end
132
+
133
+ # Returns an array of all lib directories for this package. By default
134
+ # this will just be `['lib']`, but additional paths may be added.
135
+ #
136
+ # Usage:
137
+ #
138
+ # package.lib_dirs
139
+ # # => ['lib', 'ext']
140
+ #
141
+ # @return [Array<String>] relative lib directories.
142
+ def lib_dirs
143
+ ['lib']
144
+ end
145
+
146
+ # Returns an array of all files in the lib directory. Vendor directories
147
+ # are currently ignored.
148
+ #
149
+ # **Note:** These will be full paths, not relative.
150
+ #
151
+ # @return [Array<String>] array of paths.
152
+ def lib_files
153
+ Dir["#{package_dir}/lib/**/*.rb"]
154
+ end
155
+
156
+ # Returns an array of the relative lib files for this package. This
157
+ # takes the fully qualified libs and removes the package path. For
158
+ # example, this will return an array such as:
159
+ #
160
+ # ['lib/my_package.rb', 'lib/my_package/version.rb', 'ext/foo.rb']
161
+ #
162
+ # @return [Array<String>] array of relative paths.
163
+ def relative_lib_files
164
+ libs_regexp = /^#{package_dir}\/(.*)$/
165
+
166
+ lib_files.map do |lib|
167
+ match = libs_regexp.match lib
168
+ raise "Something went wrong in `relative_lib_files`" unless match
169
+
170
+ match[1]
171
+ end
172
+ end
173
+
174
+ # @return [Hash] returns a hash version of the package which can then
175
+ # be written to a yaml file.
176
+ def to_yaml
177
+ validate_package!
178
+
179
+ hash = {
180
+ 'name' => name,
181
+ 'version' => version.to_s
182
+ }
183
+
184
+ deps = {}
185
+ dev_deps = {}
186
+
187
+ dependencies.each { |dep| deps[dep.name] = dep.version.to_s }
188
+ dev_dependencies.each { |dep| dev_deps[dep.name] = dep.version.to_s }
189
+
190
+ hash['dependencies'] = deps
191
+ hash['dev_dependencies'] = dev_deps
192
+
193
+ hash.to_yaml
194
+ end
195
+
196
+ # Ensure this package is valid - needs to be done before install or
197
+ # before writing to file.
198
+ #
199
+ # FIXME: need to implement
200
+ def validate_package!
201
+ true
202
+ end
203
+
204
+ # Create a Package from the given gemspec file
205
+ def self.from_gemspec(spec)
206
+ p = self.new
207
+
208
+ p.name = spec.name
209
+ p.version = spec.version
210
+
211
+ spec.runtime_dependencies.each do |d|
212
+ p.dependencies << Dependency.new(d.name, d.requirement)
213
+ end
214
+
215
+ spec.development_dependencies.each do |d|
216
+ p.dev_dependencies << Dependency.new(d.name, d.requirement)
217
+ end
218
+
219
+ p.vendor_lib_paths = spec.require_paths.reject { |p| p == 'lib' }
220
+
221
+ p
222
+ end
223
+ end
224
+ end
225
+
File without changes
@@ -0,0 +1,198 @@
1
+ require 'fileutils'
2
+
3
+ module Rbp
4
+ # PackageManager is used for installing, updating and managing packages
5
+ # installed locally inside an app/development package.
6
+ #
7
+ # The package manager can either be used through the command interface,
8
+ # or directly, but the cli tools are the recomended approach.
9
+ class PackageManager
10
+ include FileUtils
11
+
12
+ # @return [Package] the package for the local context
13
+ attr_reader :package
14
+
15
+ # @return [Array<Package>] an array of locally installed packages
16
+ attr_reader :installed_packages
17
+
18
+ # @return [Hash<String, Dependency>] a hash of packnames to their
19
+ # dependency. The top package registers its dependencies here, and
20
+ # as those are added then their dependencies are added recursively.
21
+ # We can use this to find dependency mismatches, i.e. when two
22
+ # packages depend on different versions of packages.
23
+ attr_reader :dependencies
24
+
25
+ # Returns a new package manager with the given root. The root is the
26
+ # local app dir which packages should be managed for. This defaults to
27
+ # the current working directory.
28
+ #
29
+ # @param [String] root the root directory
30
+ def initialize(root = Dir.getwd)
31
+ @root = root
32
+ @dependencies = {}
33
+
34
+ if @package = load_package(File.join(@root, 'package.yml'))
35
+ # register_package @package
36
+ end
37
+
38
+ # find installed packages
39
+ @installed_packages = Dir['vendor/packages/descriptors/*.yml'].map do |p|
40
+ Package.load_path p
41
+ end
42
+ end
43
+ # @return [String] the directory where our dependencies go within the
44
+ # local app dir. This is 'vendor/packages'.
45
+ def dependencies_dir
46
+ @dependencies_dir ||= File.join @root, 'vendor', 'packages'
47
+ end
48
+
49
+ # @return [String] the directory where all the package.yml for locally
50
+ # installed packages go. This is '$dependencies_dir/descriptors'
51
+ def descriptors_dir
52
+ @descriptors_dir ||= File.join dependencies_dir, 'descriptors'
53
+ end
54
+
55
+ # @return [String] the directory where actual packages are installed
56
+ # to. This is '$dependencies_dir/packages'
57
+ def packages_dir
58
+ @packages_dir ||= File.join dependencies_dir, 'packages'
59
+ end
60
+
61
+ # @return [String] the file where all the load paths are stored and
62
+ # manipulated. This is '$dependencies/init.rb'
63
+ def init_file
64
+ @init_file ||= File.join dependencies_dir, 'init.rb'
65
+ end
66
+
67
+ # @return [String] the directory where a package with the given name
68
+ # should be installed to. Will be '$dependencies/packages/$name'
69
+ def package_dir_for(name)
70
+ File.join packages_dir, name
71
+ end
72
+
73
+ # @return [String] the file where a packages' yaml will be written to.
74
+ # This will be `$dependencies/descriptors/$name.yml'
75
+ def descriptor_file_for(name)
76
+ File.join descriptors_dir, "#{name}.yml"
77
+ end
78
+
79
+ # Tries to load a package with the given name in the given directory.
80
+ # If it can't, it returns nil. We don't throw an error as some methods
81
+ # use this just to see if one exists.
82
+ #
83
+ # @param [String] path the full path to the package
84
+ # @return [Package] the package loaded
85
+ def load_package(path)
86
+ begin
87
+ Package.load_path path
88
+ rescue PackageNotFoundError => e
89
+ nil
90
+ end
91
+ end
92
+
93
+ # Install all of the dependencies for this package recursively. This
94
+ # will start with the top level package and install all of its
95
+ # packages, and then those packages' dependencies.
96
+ #
97
+ # If a dependency has already been installed, then it is skipped.
98
+ def install_dependencies
99
+ raise "Cannot find root package" unless @package
100
+
101
+ bootstrap
102
+
103
+ install_dependencies_for @package, true
104
+ end
105
+
106
+ # do the work for a given package..
107
+ def install_dependencies_for(package, include_dev = false)
108
+ deps = package.dependencies
109
+ deps += package.dev_dependencies if include_dev
110
+
111
+ puts "* Need to install for `#{package.name}'"
112
+
113
+ deps.each do |dep|
114
+ if installed? dep.name
115
+ log "Skipping #{dep.name}"
116
+ next
117
+ end
118
+
119
+ pkg = install_dependency dep
120
+ puts "installed: #{pkg}"
121
+
122
+ install_dependencies_for pkg
123
+ end
124
+ end
125
+
126
+ # Checkes whether a package with the given name has been installed or
127
+ # not. Returns true/false.
128
+ def installed?(name)
129
+ @installed_packages.each { |p| return true if p.name == name }
130
+
131
+ false
132
+ end
133
+
134
+ private
135
+
136
+ # @return [Package] returns the package for the installed dependency
137
+ def install_dependency(dependency = nil, force = false)
138
+
139
+ name = dependency.name
140
+
141
+ if installed? name
142
+ raise "Already installed `#{name}'" unless force
143
+
144
+ uninstaller = DependencyUninstaller.new name
145
+
146
+ log "Uninstalling `#{name}'"
147
+ uninstaller.uninstall self
148
+ end
149
+
150
+ installer = DependencyInstaller.new dependency
151
+
152
+ log "Installing `#{name}'"
153
+ package = installer.install self
154
+
155
+ return package
156
+ end
157
+
158
+ # This setups the local environment by ensuring that the packages/
159
+ # directory and init.rb files are ready to use. This should not be
160
+ # called directly as methods that require the environment call it
161
+ # automatically.
162
+ #
163
+ # The `force` option can be used to reset the environment. This will
164
+ # simply remove the existing packages directory, and recreate all
165
+ # needed files. This will therefore require all dependencies to be
166
+ # re-installed locally.
167
+ #
168
+ # @param [Boolean] force whether to clear the environment to start
169
+ # from scratch.
170
+ def bootstrap(force = false)
171
+ packages_dir = self.packages_dir
172
+ init_file = self.init_file
173
+
174
+ rm_rf packages_dir if force and File.exists? packages_dir
175
+ return if File.exists?(packages_dir) && File.exists?(init_file)
176
+
177
+ mkdir_p packages_dir
178
+
179
+ File.open(init_file, 'w+') do |o|
180
+ init = <<-OUT
181
+ # root packages' path
182
+ path = File.expand_path('..', __FILE__)
183
+
184
+ # root packages' lib directory
185
+ $:.unshift File.expand_path("\#{path}/../../lib")
186
+ OUT
187
+
188
+ o.write init.each_line.map { |l| l.lstrip }.join("")
189
+ end
190
+ end
191
+
192
+ # Simple log we can control with @verbose
193
+ def log(str)
194
+ puts str
195
+ end
196
+ end
197
+ end
198
+
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rbp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Adam Beynon
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-09-22 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: A ruby package manager for managing local packages
15
+ email:
16
+ - adam@adambeynon.com
17
+ executables:
18
+ - rbp
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - bin/rbp
23
+ - lib/rbp/command.rb
24
+ - lib/rbp/dependency.rb
25
+ - lib/rbp/dependency_installer.rb
26
+ - lib/rbp/dependency_uninstaller.rb
27
+ - lib/rbp/git_dependency.rb
28
+ - lib/rbp/installer.rb
29
+ - lib/rbp/package.rb
30
+ - lib/rbp/package_installer.rb
31
+ - lib/rbp/package_manager.rb
32
+ - lib/rbp.rb
33
+ - README.md
34
+ homepage: http://opalscript.org
35
+ licenses: []
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.10
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: A ruby package manager for managing local packages
58
+ test_files: []