Class: Bezel

Inherits:
Module
  • Object
show all
Defined in:
lib/bezel.rb

Overview

The idea of Bezel is to overcome the limitations of using different versions of the same package in the same Ruby proccess.

It works like this. Let’s say I wrote a library called TomsLib. Now I want to use TomsLib in my new fancy app, FancyApp. In my FancyApp namespace I have to create a reference to TomsLib.

module FancyApp
  TomsLib = lib('tomslib', '1.5')
  ...

Now I have access to TomsLib, but it is localized to my application. If Jane comes along and wants to use a different version of TomsLib but also utilizes my FancyApp, she could do so:

module JanesProgram
  TomsLib  = lib('tomslib', '1.0')
  FancyApp = lib('fancyapp')  # use newest available
  ...

How does this work? When you call lib(), Bezel looks for the package in the current gem paths (and, in the future, Roll ledger) then it reads the bezel file (e.g. lib/tomslib.rbz) from the package and evals it into an anonymous module.

This has a some important effects on how you write your Ruby programs:

  1. Any reference to core/standard libraries must be referenced via toplevel ‘::` prefix (eg. ::Enumerable).

  2. Core extensions are not version controlled. So avoid them when possible, or depend on highly stable standardized bases such as Facets and ActiveSupport.

  3. Since Bezel is a completely different alternative to Ruby’s normal load system, your program will require Bezel be installed by your users.

Despite these necessary practices for its use, Bezel is highly advantageous to developers and end-users alike in that it puts an end to the dreaded Dependency Hell.

TODO: Consider how best to support alternate loadpaths beyond ‘lib/’.

Constant Summary collapse

TABLE =

Cache of loaded modules. This speeds things up if two libraries load the same third library.

Hash.new
SCRIPT =

Script cache.

{}
STACK =

Load stack keeps track of what modules are in the process of being loaded.

[]

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, version) ⇒ Bezel

Construct new Bezel module.



130
131
132
133
134
135
136
# File 'lib/bezel.rb', line 130

def initialize(name, version) #, path)
  @__name__     = name
  @__version__  = version
  #@__path__     = path
  @__features__ = []
  super()
end

Class Method Details

.development=(boolean) ⇒ Object

When in development mode, Bezel uses the latest and greatest available version regardless of version settings.



63
64
65
# File 'lib/bezel.rb', line 63

def self.development=(boolean)
  @development = !!boolean
end

.development?Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/bezel.rb', line 68

def self.development?
  @development
end

.lib(name, version = nil) ⇒ Object

Load library into a module namespace.

Raises:

  • (LoadError)


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/bezel.rb', line 73

def self.lib(name, version=nil)
  version = nil if development?

  ##path = find(name, version)
  ##raise LoadError, "#{name}-#{version} not found" unless path
  ## location of main requirement file
  ## TODO: should we require a dedicated bezel file instead? e.g. `*.rbz`.
  #main = File.join(path, 'lib', name + '.rb')  #.rbz #TODO: LOADPATH

  main = Find.feature(name, :from=>name, :version=>version, :absolute=>true).first

  ## check cache
  return TABLE[main] if TABLE.key?(main)

  ## load error if no bezel file
  raise LoadError, "#{name}-#{version} has no bezel!" unless main && File.exist?(main)

  ## read in bezel file
  #script = File.read(main)

  ## create new Bezel module for file
  mod = new(name, version) #, main)

  ## put module on STACK
  STACK.push mod

  ## evaluate script in the context of module
  #mod.module_eval(script, main, 0)  # r =
  mod.__load_feature__(main)

  ## pop module off STACK
  STACK.pop

  ## add module to cache, and return module
  TABLE[main] = mod
end

.require(path) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
# File 'lib/bezel.rb', line 111

def self.require(path)
  if current = STACK.last
    begin
      current.__load_feature__(path) ? true : false
    rescue LoadError
      require_without_bezel(path)
    end
  else
    require_without_bezel(path)
  end
end

Instance Method Details

#__features__Object



154
155
156
# File 'lib/bezel.rb', line 154

def __features__
  @__features__
end

#__load_feature__(path) ⇒ Object

Raises:

  • (LoadError)


159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/bezel.rb', line 159

def __load_feature__(path)
  if path =~ /^[\.\/]/  # if absolute path
    file = File.expand_path(path)
  else
    file = Find.feature(path, :from=>__name__, :version=>__version__, :absolute=>true).first
  end

  raise LoadError, "no such file to load -- #{file}" unless file

  if __features__.include?(file)
    return false
  else
    __features__ << file
    script = File.read(file) #(SCRIPT[file] ||= File.read(file))
    module_eval(script, file, 0)
  end

  return file
end

#__name__Object

Name of library.



139
140
141
# File 'lib/bezel.rb', line 139

def __name__
  @__name__
end

#__version__Object

Version of library.



144
145
146
# File 'lib/bezel.rb', line 144

def __version__
  @__version__
end