├── var ├── name ├── title ├── version ├── created ├── organization ├── summary ├── authors ├── copyrights ├── repositories ├── description ├── requirements └── resources ├── pkg ├── .gitignore └── backload.gemspec ├── test ├── fixtures │ ├── example_load.rb │ ├── example_relative.rb │ └── example_require.rb ├── test_load.rb ├── test_require.rb ├── test_require_relative.rb └── helper.rb ├── .gitignore ├── Rakefile ├── Gemfile ├── .travis.yml ├── lib ├── backload │ ├── autoload.rb │ ├── load.rb │ ├── require.rb │ └── require_relative.rb └── backload.rb ├── MANIFEST.txt ├── Toolchain ├── HISTORY.md ├── .index ├── LICENSE.txt └── README.md /var/name: -------------------------------------------------------------------------------- 1 | backload 2 | -------------------------------------------------------------------------------- /var/title: -------------------------------------------------------------------------------- 1 | Backload 2 | -------------------------------------------------------------------------------- /var/version: -------------------------------------------------------------------------------- 1 | 0.2.0 2 | -------------------------------------------------------------------------------- /var/created: -------------------------------------------------------------------------------- 1 | 2012-04-29 2 | -------------------------------------------------------------------------------- /pkg/.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.gz 3 | -------------------------------------------------------------------------------- /var/organization: -------------------------------------------------------------------------------- 1 | --- 2 | - Rubyworks 3 | -------------------------------------------------------------------------------- /var/summary: -------------------------------------------------------------------------------- 1 | Callbacks for #require and #load. 2 | -------------------------------------------------------------------------------- /test/fixtures/example_load.rb: -------------------------------------------------------------------------------- 1 | # example ruby file 2 | 1 + 1 3 | -------------------------------------------------------------------------------- /var/authors: -------------------------------------------------------------------------------- 1 | --- 2 | - Thomas Sawyer 3 | -------------------------------------------------------------------------------- /var/copyrights: -------------------------------------------------------------------------------- 1 | --- 2 | - 2012 Rubyworks (BSD-2-Clause) 3 | 4 | -------------------------------------------------------------------------------- /test/fixtures/example_relative.rb: -------------------------------------------------------------------------------- 1 | # example ruby file 2 | 1 + 1 3 | -------------------------------------------------------------------------------- /test/fixtures/example_require.rb: -------------------------------------------------------------------------------- 1 | # example ruby file 2 | 1 + 1 3 | -------------------------------------------------------------------------------- /var/repositories: -------------------------------------------------------------------------------- 1 | --- 2 | upstream: git://github.com/rubyworks/backload.git 3 | -------------------------------------------------------------------------------- /var/description: -------------------------------------------------------------------------------- 1 | Backload provides callbacks for Ruby's #require and #load methods. 2 | -------------------------------------------------------------------------------- /var/requirements: -------------------------------------------------------------------------------- 1 | --- 2 | - microtest (test) 3 | - detroit (build) 4 | - rake (build) 5 | - rubytest-cli (test) 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .erog/digest 2 | .yardoc 3 | doc/ 4 | log/ 5 | tmp/ 6 | site/ 7 | web/ 8 | work/sandbox 9 | DEMO.md 10 | *.lock 11 | *.gem 12 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | task :default => [:test] 4 | 5 | desc "Run unit tests." 6 | task :test do 7 | sh "rubytest -Ilib test/test_*" 8 | end 9 | -------------------------------------------------------------------------------- /var/resources: -------------------------------------------------------------------------------- 1 | --- 2 | home: http://rubyworks.github.com/backload 3 | code: http://github.com/rubyworks/backload 4 | bugs: http://github.com/rubyworks/backload/issues 5 | 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec :path => 'pkg' 3 | 4 | platforms :rbx do 5 | #gem 'racc' 6 | gem 'rubysl', '~> 2.0' 7 | gem 'psych' 8 | end 9 | 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: ruby 3 | script: "bundle exec rubytest -Ilib test/test_*.rb" 4 | rvm: 5 | - 1.9.3 6 | - 2.0.0 7 | - 2.1.0 8 | - rbx 9 | - jruby-19mode 10 | 11 | -------------------------------------------------------------------------------- /lib/backload/autoload.rb: -------------------------------------------------------------------------------- 1 | # This does not look doable in pure Ruby. 2 | 3 | #module Kernel 4 | # 5 | # # 6 | # # We create a noop method b/c it simplifes implementation. 7 | # # Just override this method to use. 8 | # # 9 | # def self.autoloaded(path, options={}) 10 | # end 11 | # 12 | #end 13 | -------------------------------------------------------------------------------- /test/test_load.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('helper', File.dirname(__FILE__)) 2 | 3 | class RequiredTestCase < MicroTest::TestCase 4 | 5 | def test_load 6 | load 'example_load.rb' 7 | 8 | assert_equal 'example_load.rb', $load_name 9 | 10 | refute $load_opts[:require] 11 | refute $load_opts[:relative] 12 | assert $load_opts[:load] 13 | refute $load_opts[:wrap] 14 | end 15 | 16 | end 17 | 18 | -------------------------------------------------------------------------------- /test/test_require.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('helper', File.dirname(__FILE__)) 2 | 3 | class RequiredTestCase < MicroTest::TestCase 4 | 5 | def test_require 6 | require 'example_require' 7 | 8 | assert_equal 'example_require', $load_name 9 | 10 | assert $load_opts[:require] 11 | refute $load_opts[:relative] 12 | refute $load_opts[:load] 13 | refute $load_opts[:wrap] 14 | end 15 | 16 | end 17 | 18 | -------------------------------------------------------------------------------- /MANIFEST.txt: -------------------------------------------------------------------------------- 1 | #!mast .yardopts .index bin demo lib man spec test try *.md *.txt 2 | .index 3 | lib/backload/autoload.rb 4 | lib/backload/load.rb 5 | lib/backload/require.rb 6 | lib/backload/require_relative.rb 7 | lib/backload.rb 8 | test/fixtures/example_load.rb 9 | test/fixtures/example_relative.rb 10 | test/fixtures/example_require.rb 11 | test/helper.rb 12 | test/test_load.rb 13 | test/test_require.rb 14 | test/test_require_relative.rb 15 | LICENSE.txt 16 | HISTORY.md 17 | README.md 18 | -------------------------------------------------------------------------------- /Toolchain: -------------------------------------------------------------------------------- 1 | --- 2 | email: 3 | file: ~ 4 | subject: ~ 5 | mailto: 6 | - ruby-talk@ruby-lang.org 7 | - rubyworks-mailinglist@googlegroups.com 8 | #parts: [readme] 9 | 10 | gem: 11 | gemspec: pkg/backload.gemspec 12 | 13 | github: 14 | gh_pages: web 15 | 16 | dnote: 17 | title: Source Notes 18 | output: log/notes.html 19 | labels: ~ 20 | 21 | yard: 22 | active: false 23 | yardopts: true 24 | priority: 2 25 | 26 | vclog: 27 | active: false 28 | output: 29 | - log/history.html 30 | - log/changes.html 31 | 32 | -------------------------------------------------------------------------------- /test/test_require_relative.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('helper', File.dirname(__FILE__)) 2 | 3 | require 'backload/require_relative' # optional feature 4 | 5 | class RequiredTestCase < MicroTest::TestCase 6 | 7 | def test_require_relative 8 | require_relative 'fixtures/example_relative' 9 | 10 | assert_equal 'fixtures/example_relative', $load_name 11 | 12 | assert $load_opts[:require] 13 | assert $load_opts[:relative] 14 | refute $load_opts[:load] 15 | refute $load_opts[:wrap] 16 | 17 | assert_equal File.dirname(__FILE__), $load_opts[:relative] 18 | end 19 | 20 | end 21 | 22 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | # Load our test framework. 2 | require 'microtest' 3 | require 'microtest/assertions' 4 | 5 | # Load the library to be tested. 6 | require 'backload' 7 | 8 | # Put our load fixtures on the load path. 9 | $LOAD_PATH.unshift(File.dirname(__FILE__) + '/fixtures') 10 | 11 | # We use this setup to test each possible load configuration. 12 | def Kernel.backloaded(name, opts) 13 | $load_name = name 14 | $load_opts = opts 15 | end 16 | 17 | # Old version of Microtest lacks #refute. 18 | module MicroTest::Assertions 19 | unless method_defined?(:refute) 20 | def refute(fact, msg=nil) 21 | __assert__(!fact, msg) 22 | end 23 | end 24 | end 25 | 26 | -------------------------------------------------------------------------------- /lib/backload.rb: -------------------------------------------------------------------------------- 1 | require 'backload/load' 2 | require 'backload/require' 3 | #require 'backload/require_relative' 4 | 5 | module Kernel 6 | 7 | # Common callback for all loading methods: #load, #require 8 | # and #require_relative. 9 | # 10 | # @param [Hash] options 11 | # The load callback options. 12 | # 13 | # @option options [Boolean] :load 14 | # True if #load was used. 15 | # 16 | # @option options [Boolean] :require 17 | # True is #require or #require_relative was used. 18 | # 19 | # @option options [Boolean] :relative 20 | # True is #require_relative was used. 21 | # 22 | # @option options [Boolean] :wrap 23 | # The wrap argument passed to #load. 24 | # 25 | def self.backloaded(path, options={}) 26 | end 27 | 28 | end 29 | 30 | -------------------------------------------------------------------------------- /lib/backload/load.rb: -------------------------------------------------------------------------------- 1 | module Kernel 2 | 3 | # 4 | # We create a noop method b/c it simplifes implementation. 5 | # Just override this method to use. 6 | # 7 | def self.loaded(path, wrap=nil) 8 | end 9 | 10 | private 11 | 12 | # 13 | # Alias original Kernel#load method. 14 | # 15 | alias_method :load_without_callback, :load 16 | 17 | # 18 | # Redefine Kernel#load with callback. 19 | # 20 | def load(path, wrap=nil) 21 | result = load_without_callback(path, wrap) 22 | 23 | if result 24 | Kernel.loaded(path, wrap) 25 | Kernel.backloaded(path, :load=>true, :require=>false, :wrap=>wrap) 26 | end 27 | 28 | result 29 | end 30 | 31 | class << self 32 | # 33 | # Alias original Kernel.load method. 34 | # 35 | alias_method :load_without_callback, :load 36 | 37 | # 38 | # Redefine Kernel.load with callback. 39 | # 40 | def load(path, wrap=nil) 41 | result = load_without_callback(path, wrap) 42 | 43 | if result 44 | Kernel.loaded(path, wrap) 45 | Kernel.backloaded(path, :load=>true, :require=>false, :wrap=>wrap) 46 | end 47 | 48 | result 49 | end 50 | end 51 | 52 | end 53 | -------------------------------------------------------------------------------- /lib/backload/require.rb: -------------------------------------------------------------------------------- 1 | module Kernel 2 | 3 | # 4 | # We create a noop method b/c it simplifes implementation. 5 | # Just override this method to use. 6 | # 7 | def self.required(path) 8 | end 9 | 10 | private 11 | 12 | # 13 | # Alias original Kernel#require method. 14 | # 15 | alias_method :require_without_callback, :require 16 | 17 | # 18 | # Redefine Kernel#require with callback. 19 | # 20 | def require(feature, options=nil) 21 | result = require_without_callback(feature) 22 | 23 | if result 24 | Kernel.required(feature) 25 | Kernel.backloaded(feature, :require=>true, :load=>false) 26 | end 27 | 28 | result 29 | end 30 | 31 | class << self 32 | # 33 | # Alias original Kernel.require method. 34 | # 35 | alias_method :require_without_callback, :require 36 | 37 | # 38 | # Redefine Kernel.require with callback. 39 | # 40 | def require(feature) 41 | result = require_without_callback(feature) 42 | 43 | if result 44 | Kernel.required(feature) 45 | Kernel.backloaded(feature, :require=>true, :load=>false) 46 | end 47 | 48 | result 49 | end 50 | end 51 | 52 | end 53 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # RELEASE HISTORY 2 | 3 | ## 0.2.0 / 2012-12-22 4 | 5 | Change the name of the project from Loaded to Backload, and 6 | changed the main callback method from `#loaded` to `#backloaded`. 7 | Then re-added `#loaded`, `#required` and `#required_relative` as 8 | straight callbacks for their corresponding present-tense methods. 9 | This makes the library backward compatible with v0.1.0. 10 | 11 | Changes: 12 | 13 | * Rename project to "backload". 14 | * Add basic callbacks for load, require and require_relative. 15 | * Master callback is renamed to `#backloaded`. 16 | 17 | 18 | ## 0.1.1 / 2012-12-19 19 | 20 | Just a tweak to require_relative support, so that the `:relative` 21 | option provides the relative path, not just `true` or `false`. 22 | 23 | Changes: 24 | 25 | * The :relative option provides the relative path. 26 | 27 | 28 | ## 0.1.0 / 2012-12-18 29 | 30 | This release uses a single callback method `#loaded` instead 31 | of the previous two (`#loaded` and `#required`). It also adds 32 | optional support for `#require_relative`. 33 | 34 | Changes: 35 | 36 | * Use single callback method. 37 | * Add support for #require_relative. 38 | 39 | 40 | ## 0.0.1 / 2012-04-29 41 | 42 | Initial release of Loaded. 43 | 44 | Changes: 45 | 46 | * Created and implemented. 47 | 48 | -------------------------------------------------------------------------------- /.index: -------------------------------------------------------------------------------- 1 | --- 2 | revision: 2013 3 | type: ruby 4 | sources: 5 | - var 6 | authors: 7 | - name: Thomas Sawyer 8 | email: transfire@gmail.com 9 | organizations: [] 10 | requirements: 11 | - groups: 12 | - test 13 | development: true 14 | name: microtest 15 | - groups: 16 | - build 17 | development: true 18 | name: detroit 19 | - groups: 20 | - build 21 | development: true 22 | name: rake 23 | - groups: 24 | - test 25 | development: true 26 | name: rubytest-cli 27 | conflicts: [] 28 | alternatives: [] 29 | resources: 30 | - type: home 31 | uri: http://rubyworks.github.com/backload 32 | label: Homepage 33 | - type: code 34 | uri: http://github.com/rubyworks/backload 35 | label: Source Code 36 | - type: bugs 37 | uri: http://github.com/rubyworks/backload/issues 38 | label: Issue Tracker 39 | repositories: 40 | - name: upstream 41 | scm: git 42 | uri: git://github.com/rubyworks/backload.git 43 | categories: [] 44 | copyrights: 45 | - holder: Rubyworks 46 | year: '2012' 47 | license: BSD-2-Clause 48 | customs: [] 49 | paths: 50 | lib: 51 | - lib 52 | name: backload 53 | title: Backload 54 | summary: 'Callbacks for #require and #load.' 55 | created: '2012-04-29' 56 | description: 'Backload provides callbacks for Ruby''s #require and #load methods.' 57 | version: 0.2.0 58 | date: '2014-01-14' 59 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Required - Shouldn't require have a callback? 2 | (http://rubyworks.github.com/required) 3 | 4 | Copyright (c) 2012 Rubyworks. All rights reserved 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are 7 | permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this list of 10 | conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 13 | of conditions and the following disclaimer in the documentation and/or other materials 14 | provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 17 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 18 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR 19 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | SPDX License: BSD-2-Clause 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Homepage](http://rubygems.org/gems/backload) | 2 | [Report Issue](http://github.com/rubyworks/backload/issues) | 3 | [Source Code](http://github.com/rubyworks/backload) 4 | ( [![Build Status](https://secure.travis-ci.org/rubyworks/backload.png)](http://travis-ci.org/rubyworks/backload) ) 5 | 6 | 7 | # Backload 8 | 9 | Shouldn't loading have a callback? 10 | 11 | 12 | ## Installation 13 | 14 | Using RubyGems type on a command line: 15 | 16 | $ gem install backload 17 | 18 | 19 | ## Instruction 20 | 21 | To use Backload simply override the `Kernel.backloaded` method. 22 | 23 | ```ruby 24 | require 'backload' 25 | require 'backload/require_relative' 26 | 27 | def Kernel.backloaded(feature, options={}) 28 | if options[:require] 29 | if rel = options[:relative] 30 | puts "#{feature} has been required relative to #{rel}!" 31 | else 32 | puts "#{feature} has been required!" 33 | end 34 | else 35 | if wrap = options[:wrap] 36 | puts "#{feature} has been loaded with wrap, it's #{wrap}!" 37 | else 38 | puts "#{feature} has been loaded!" 39 | end 40 | end 41 | end 42 | ``` 43 | 44 | Becuase of implementation details, `#require_relative` has to be reimplemented completely 45 | to make the callback work. To be on the safe side, at least for now, it therefore has to 46 | be required separately, as the example above demonstrates. 47 | 48 | Backload also provides callbacks for each type of loading. Just use the past tense 49 | of the term for any of `#load`, `#require` and `#require_relative`. For example, to 50 | see all features as they have been required. 51 | 52 | ```ruby 53 | def Kernel.required(feature) 54 | puts "#{feature} required!" 55 | end 56 | ``` 57 | 58 | ## Feedback 59 | 60 | Please report issues or suggestions to 61 | [GitHub Issues](http://github.com/rubyworks/backload/issues). 62 | Or if you wish to discuss in real-time try [IRC #rubyworks](irc://chat.us.freenet.org/rubyworks) on freenode.net. 63 | 64 | 65 | ## Copying 66 | 67 | Backload is copyrighted open source software. 68 | 69 | Copyright 2012 Rubyworks. 70 | 71 | It is modifiable and redistributable in accordance with the 72 | **BSD-2-Clause** license. 73 | 74 | See LICENSE.txt for details. 75 | -------------------------------------------------------------------------------- /lib/backload/require_relative.rb: -------------------------------------------------------------------------------- 1 | module Kernel 2 | 3 | # 4 | # We create a noop method b/c it simplifes implementation. 5 | # Just override this method to use. 6 | # 7 | # Presently this is an optional feature and must be required 8 | # separately via `backload/require_relative`. 9 | # 10 | def self.required_relative(path) 11 | end 12 | 13 | private 14 | 15 | # 16 | # Alias original Kernel#require_relative method. 17 | # 18 | alias_method :require_relative_without_callback, :require_relative 19 | 20 | # 21 | # Redefine Kernel#require_relative with callback. 22 | # 23 | # @todo It would nice if the `:relative` option were set to the 24 | # realtive path, but that would require `Binding.of_caller`. 25 | # 26 | def require_relative(feature) 27 | #result = require_relative_without_callback(feature) 28 | 29 | ## We have to reimplement #require_relative instead of calling 30 | ## the alias to ensure the proper path is used. (*sad*) 31 | result = ( 32 | c = caller.first 33 | fail "Can't parse #{c}" unless c.rindex(/:\d+(:in `.*')?$/) 34 | file = $` # File.dirname(c) 35 | if /\A\((.*)\)/ =~ file # eval, etc. 36 | raise LoadError, "require_relative is called in #{$1}" 37 | end 38 | #options[:relative] = File.dirname(file) 39 | absolute = File.expand_path(feature, File.dirname(file)) 40 | require_without_callback(absolute) 41 | ) 42 | 43 | if result 44 | Kernel.required_relative(feature) 45 | Kernel.backloaded(feature, :require=>true, :load=>false, :relative=>File.dirname(file)) 46 | end 47 | 48 | result 49 | end 50 | 51 | class << self 52 | # 53 | # Alias original Kernel.require_relative method. 54 | # 55 | alias_method :require_relative_without_callback, :require_relative 56 | 57 | # 58 | # Redefine Kernel.require_relative with callback. 59 | # 60 | # @todo It would nice if the `:relative` option were set to the 61 | # realtive path, but that would require `Binding.of_caller`. 62 | # 63 | def require_relative(feature) 64 | #result = require_relative_without_callback(feature) 65 | 66 | ## We have to reimplement #require_relative instead of calling 67 | ## the alias to ensure the proper path is used. (*sad*) 68 | result = ( 69 | c = caller.first 70 | fail "Can't parse #{c}" unless c.rindex(/:\d+(:in `.*')?$/) 71 | file = $` # File.dirname(c) 72 | if /\A\((.*)\)/ =~ file # eval, etc. 73 | raise LoadError, "require_relative is called in #{$1}" 74 | end 75 | #options[:relative] = File.dirname(file) 76 | absolute = File.expand_path(feature, File.dirname(file)) 77 | require_without_callback absolute 78 | ) 79 | 80 | if result 81 | Kernel.required_relative(feature) 82 | Kernel.backloaded(feature, :require=>true, :load=>false, :relative=>File.dirname(file)) 83 | end 84 | 85 | result 86 | end 87 | end 88 | 89 | end 90 | -------------------------------------------------------------------------------- /pkg/backload.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'yaml' 4 | require 'pathname' 5 | 6 | module Indexer 7 | 8 | # Convert index data into a gemspec. 9 | # 10 | # Notes: 11 | # * Assumes all executables are in bin/. 12 | # * Does not yet handle default_executable setting. 13 | # * Does not yet handle platform setting. 14 | # * Does not yet handle required_ruby_version. 15 | # * Support for rdoc entries is weak. 16 | # 17 | class GemspecExporter 18 | 19 | # File globs to include in package --unless a manifest file exists. 20 | FILES = ".index .yardopts alt bin data demo ext features lib man spec test try* [A-Z]*.*" unless defined?(FILES) 21 | 22 | # File globs to omit from FILES. 23 | OMIT = "Config.rb" unless defined?(OMIT) 24 | 25 | # Standard file patterns. 26 | PATTERNS = { 27 | :root => '{.index,Gemfile}', 28 | :bin => 'bin/*', 29 | :lib => 'lib/{**/}*', #.rb', 30 | :ext => 'ext/{**/}extconf.rb', 31 | :doc => '*.{txt,rdoc,md,markdown,tt,textile}', 32 | :test => '{test,spec}/{**/}*.rb' 33 | } unless defined?(PATTERNS) 34 | 35 | # For which revision of indexer spec is this converter intended? 36 | REVISION = 2013 unless defined?(REVISION) 37 | 38 | # 39 | def self.gemspec 40 | new.to_gemspec 41 | end 42 | 43 | # 44 | attr :metadata 45 | 46 | # 47 | def initialize(metadata=nil) 48 | @root_check = false 49 | 50 | if metadata 51 | root_dir = metadata.delete(:root) 52 | if root_dir 53 | @root = root_dir 54 | @root_check = true 55 | end 56 | metadata = nil if metadata.empty? 57 | end 58 | 59 | @metadata = metadata || YAML.load_file(root + '.index') 60 | 61 | if @metadata['revision'].to_i != REVISION 62 | warn "This gemspec exporter was not designed for this revision of index metadata." 63 | end 64 | end 65 | 66 | # 67 | def has_root? 68 | root ? true : false 69 | end 70 | 71 | # 72 | def root 73 | return @root if @root || @root_check 74 | @root_check = true 75 | @root = find_root 76 | end 77 | 78 | # 79 | def manifest 80 | return nil unless root 81 | @manifest ||= Dir.glob(root + 'manifest{,.txt}', File::FNM_CASEFOLD).first 82 | end 83 | 84 | # 85 | def scm 86 | return nil unless root 87 | @scm ||= %w{git hg}.find{ |m| (root + ".#{m}").directory? }.to_sym 88 | end 89 | 90 | # 91 | def files 92 | return [] unless root 93 | @files ||= \ 94 | if manifest 95 | File.readlines(manifest). 96 | map{ |line| line.strip }. 97 | reject{ |line| line.empty? || line[0,1] == '#' } 98 | else 99 | list = [] 100 | Dir.chdir(root) do 101 | FILES.split(/\s+/).each do |pattern| 102 | list.concat(glob(pattern)) 103 | end 104 | OMIT.split(/\s+/).each do |pattern| 105 | list = list - glob(pattern) 106 | end 107 | end 108 | list 109 | end.select{ |path| File.file?(path) }.uniq 110 | end 111 | 112 | # 113 | def glob_files(pattern) 114 | return [] unless root 115 | Dir.chdir(root) do 116 | Dir.glob(pattern).select do |path| 117 | File.file?(path) && files.include?(path) 118 | end 119 | end 120 | end 121 | 122 | def patterns 123 | PATTERNS 124 | end 125 | 126 | def executables 127 | @executables ||= \ 128 | glob_files(patterns[:bin]).map do |path| 129 | File.basename(path) 130 | end 131 | end 132 | 133 | def extensions 134 | @extensions ||= \ 135 | glob_files(patterns[:ext]).map do |path| 136 | File.basename(path) 137 | end 138 | end 139 | 140 | def name 141 | metadata['name'] || metadata['title'].downcase.gsub(/\W+/,'_') 142 | end 143 | 144 | def homepage 145 | page = ( 146 | metadata['resources'].find{ |r| r['type'] =~ /^home/i } || 147 | metadata['resources'].find{ |r| r['name'] =~ /^home/i } || 148 | metadata['resources'].find{ |r| r['name'] =~ /^web/i } 149 | ) 150 | page ? page['uri'] : false 151 | end 152 | 153 | def licenses 154 | metadata['copyrights'].map{ |c| c['license'] }.compact 155 | end 156 | 157 | def require_paths 158 | paths = metadata['paths'] || {} 159 | paths['load'] || ['lib'] 160 | end 161 | 162 | # 163 | # Convert to gemnspec. 164 | # 165 | def to_gemspec 166 | if has_root? 167 | Gem::Specification.new do |gemspec| 168 | to_gemspec_data(gemspec) 169 | to_gemspec_paths(gemspec) 170 | end 171 | else 172 | Gem::Specification.new do |gemspec| 173 | to_gemspec_data(gemspec) 174 | to_gemspec_paths(gemspec) 175 | end 176 | end 177 | end 178 | 179 | # 180 | # Convert pure data settings. 181 | # 182 | def to_gemspec_data(gemspec) 183 | gemspec.name = name 184 | gemspec.version = metadata['version'] 185 | gemspec.summary = metadata['summary'] 186 | gemspec.description = metadata['description'] 187 | 188 | metadata['authors'].each do |author| 189 | gemspec.authors << author['name'] 190 | 191 | if author.has_key?('email') 192 | if gemspec.email 193 | gemspec.email << author['email'] 194 | else 195 | gemspec.email = [author['email']] 196 | end 197 | end 198 | end 199 | 200 | gemspec.licenses = licenses 201 | 202 | requirements = metadata['requirements'] || [] 203 | requirements.each do |req| 204 | next if req['optional'] 205 | next if req['external'] 206 | 207 | name = req['name'] 208 | groups = req['groups'] || [] 209 | 210 | version = gemify_version(req['version']) 211 | 212 | if groups.empty? or groups.include?('runtime') 213 | # populate runtime dependencies 214 | if gemspec.respond_to?(:add_runtime_dependency) 215 | gemspec.add_runtime_dependency(name,*version) 216 | else 217 | gemspec.add_dependency(name,*version) 218 | end 219 | else 220 | # populate development dependencies 221 | if gemspec.respond_to?(:add_development_dependency) 222 | gemspec.add_development_dependency(name,*version) 223 | else 224 | gemspec.add_dependency(name,*version) 225 | end 226 | end 227 | end 228 | 229 | # convert external dependencies into gemspec requirements 230 | requirements.each do |req| 231 | next unless req['external'] 232 | gemspec.requirements << ("%s-%s" % req.values_at('name', 'version')) 233 | end 234 | 235 | gemspec.homepage = homepage 236 | gemspec.require_paths = require_paths 237 | gemspec.post_install_message = metadata['install_message'] 238 | end 239 | 240 | # 241 | # Set gemspec settings that require a root directory path. 242 | # 243 | def to_gemspec_paths(gemspec) 244 | gemspec.files = files 245 | gemspec.extensions = extensions 246 | gemspec.executables = executables 247 | 248 | if Gem::VERSION < '1.7.' 249 | gemspec.default_executable = gemspec.executables.first 250 | end 251 | 252 | gemspec.test_files = glob_files(patterns[:test]) 253 | 254 | unless gemspec.files.include?('.document') 255 | gemspec.extra_rdoc_files = glob_files(patterns[:doc]) 256 | end 257 | end 258 | 259 | # 260 | # Return a copy of this file. This is used to generate a local 261 | # .gemspec file that can automatically read the index file. 262 | # 263 | def self.source_code 264 | File.read(__FILE__) 265 | end 266 | 267 | private 268 | 269 | def find_root 270 | root_files = patterns[:root] 271 | if Dir.glob(root_files).first 272 | Pathname.new(Dir.pwd) 273 | elsif Dir.glob("../#{root_files}").first 274 | Pathname.new(Dir.pwd).parent 275 | else 276 | #raise "Can't find root of project containing `#{root_files}'." 277 | warn "Can't find root of project containing `#{root_files}'." 278 | nil 279 | end 280 | end 281 | 282 | def glob(pattern) 283 | if File.directory?(pattern) 284 | Dir.glob(File.join(pattern, '**', '*')) 285 | else 286 | Dir.glob(pattern) 287 | end 288 | end 289 | 290 | def gemify_version(version) 291 | case version 292 | when /^(.*?)\+$/ 293 | ">= #{$1}" 294 | when /^(.*?)\-$/ 295 | "< #{$1}" 296 | when /^(.*?)\~$/ 297 | "~> #{$1}" 298 | else 299 | version 300 | end 301 | end 302 | 303 | end 304 | 305 | end 306 | 307 | Indexer::GemspecExporter.gemspec --------------------------------------------------------------------------------