librarianp 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +255 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +235 -0
- data/LICENSE.txt +22 -0
- data/README.md +55 -0
- data/Rakefile +28 -0
- data/VERSION +1 -0
- data/lib/librarian/action/base.rb +24 -0
- data/lib/librarian/action/clean.rb +44 -0
- data/lib/librarian/action/ensure.rb +24 -0
- data/lib/librarian/action/install.rb +95 -0
- data/lib/librarian/action/persist_resolution_mixin.rb +51 -0
- data/lib/librarian/action/resolve.rb +46 -0
- data/lib/librarian/action/update.rb +44 -0
- data/lib/librarian/action.rb +5 -0
- data/lib/librarian/algorithms.rb +133 -0
- data/lib/librarian/cli/manifest_presenter.rb +89 -0
- data/lib/librarian/cli.rb +225 -0
- data/lib/librarian/config/database.rb +205 -0
- data/lib/librarian/config/file_source.rb +47 -0
- data/lib/librarian/config/hash_source.rb +33 -0
- data/lib/librarian/config/source.rb +149 -0
- data/lib/librarian/config.rb +7 -0
- data/lib/librarian/dependency.rb +153 -0
- data/lib/librarian/dsl/receiver.rb +42 -0
- data/lib/librarian/dsl/target.rb +171 -0
- data/lib/librarian/dsl.rb +102 -0
- data/lib/librarian/environment/runtime_cache.rb +101 -0
- data/lib/librarian/environment.rb +230 -0
- data/lib/librarian/error.rb +4 -0
- data/lib/librarian/helpers.rb +29 -0
- data/lib/librarian/linter/source_linter.rb +55 -0
- data/lib/librarian/lockfile/compiler.rb +66 -0
- data/lib/librarian/lockfile/parser.rb +123 -0
- data/lib/librarian/lockfile.rb +29 -0
- data/lib/librarian/logger.rb +46 -0
- data/lib/librarian/manifest.rb +146 -0
- data/lib/librarian/manifest_set.rb +150 -0
- data/lib/librarian/mock/cli.rb +19 -0
- data/lib/librarian/mock/dsl.rb +15 -0
- data/lib/librarian/mock/environment.rb +21 -0
- data/lib/librarian/mock/extension.rb +9 -0
- data/lib/librarian/mock/source/mock/registry.rb +83 -0
- data/lib/librarian/mock/source/mock.rb +80 -0
- data/lib/librarian/mock/source.rb +1 -0
- data/lib/librarian/mock/version.rb +5 -0
- data/lib/librarian/mock.rb +1 -0
- data/lib/librarian/posix.rb +129 -0
- data/lib/librarian/resolution.rb +46 -0
- data/lib/librarian/resolver/implementation.rb +238 -0
- data/lib/librarian/resolver.rb +94 -0
- data/lib/librarian/rspec/support/cli_macro.rb +120 -0
- data/lib/librarian/source/basic_api.rb +45 -0
- data/lib/librarian/source/git/repository.rb +193 -0
- data/lib/librarian/source/git.rb +172 -0
- data/lib/librarian/source/local.rb +54 -0
- data/lib/librarian/source/path.rb +56 -0
- data/lib/librarian/source.rb +2 -0
- data/lib/librarian/spec.rb +13 -0
- data/lib/librarian/spec_change_set.rb +173 -0
- data/lib/librarian/specfile.rb +19 -0
- data/lib/librarian/support/abstract_method.rb +21 -0
- data/lib/librarian/ui.rb +64 -0
- data/lib/librarian/version.rb +3 -0
- data/lib/librarian.rb +11 -0
- data/librarian.gemspec +47 -0
- data/spec/functional/cli_spec.rb +27 -0
- data/spec/functional/posix_spec.rb +32 -0
- data/spec/functional/source/git/repository_spec.rb +199 -0
- data/spec/functional/source/git_spec.rb +174 -0
- data/spec/support/fakefs.rb +37 -0
- data/spec/support/method_patch_macro.rb +30 -0
- data/spec/support/project_path_macro.rb +14 -0
- data/spec/support/with_env_macro.rb +22 -0
- data/spec/unit/action/base_spec.rb +18 -0
- data/spec/unit/action/clean_spec.rb +102 -0
- data/spec/unit/action/ensure_spec.rb +37 -0
- data/spec/unit/action/install_spec.rb +111 -0
- data/spec/unit/algorithms_spec.rb +131 -0
- data/spec/unit/config/database_spec.rb +320 -0
- data/spec/unit/dependency/requirement_spec.rb +12 -0
- data/spec/unit/dependency_spec.rb +212 -0
- data/spec/unit/dsl_spec.rb +173 -0
- data/spec/unit/environment/runtime_cache_spec.rb +73 -0
- data/spec/unit/environment_spec.rb +209 -0
- data/spec/unit/lockfile/parser_spec.rb +162 -0
- data/spec/unit/lockfile_spec.rb +65 -0
- data/spec/unit/manifest/version_spec.rb +11 -0
- data/spec/unit/manifest_set_spec.rb +202 -0
- data/spec/unit/manifest_spec.rb +36 -0
- data/spec/unit/mock/environment_spec.rb +25 -0
- data/spec/unit/mock/source/mock_spec.rb +22 -0
- data/spec/unit/resolver_spec.rb +299 -0
- data/spec/unit/source/git_spec.rb +29 -0
- data/spec/unit/spec_change_set_spec.rb +169 -0
- metadata +257 -0
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
module Librarian
|
4
|
+
class Dependency
|
5
|
+
|
6
|
+
class Requirement
|
7
|
+
def initialize(*args)
|
8
|
+
args = initialize_normalize_args(args)
|
9
|
+
|
10
|
+
self.backing = Gem::Requirement.create(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_gem_requirement
|
14
|
+
backing
|
15
|
+
end
|
16
|
+
|
17
|
+
def satisfied_by?(version)
|
18
|
+
to_gem_requirement.satisfied_by?(version.to_gem_version)
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
to_gem_requirement == other.to_gem_requirement
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
to_gem_requirement.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
"#<#{self.class} #{to_s}>"
|
31
|
+
end
|
32
|
+
|
33
|
+
COMPATS_TABLE = {
|
34
|
+
%w(= = ) => lambda{|s, o| s == o},
|
35
|
+
%w(= !=) => lambda{|s, o| s != o},
|
36
|
+
%w(= > ) => lambda{|s, o| s > o},
|
37
|
+
%w(= < ) => lambda{|s, o| s < o},
|
38
|
+
%w(= >=) => lambda{|s, o| s >= o},
|
39
|
+
%w(= <=) => lambda{|s, o| s <= o},
|
40
|
+
%w(= ~>) => lambda{|s, o| s >= o && s.release < o.bump},
|
41
|
+
%w(!= !=) => true,
|
42
|
+
%w(!= > ) => true,
|
43
|
+
%w(!= < ) => true,
|
44
|
+
%w(!= >=) => true,
|
45
|
+
%w(!= <=) => true,
|
46
|
+
%w(!= ~>) => true,
|
47
|
+
%w(> > ) => true,
|
48
|
+
%w(> < ) => lambda{|s, o| s < o},
|
49
|
+
%w(> >=) => true,
|
50
|
+
%w(> <=) => lambda{|s, o| s < o},
|
51
|
+
%w(> ~>) => lambda{|s, o| s < o.bump},
|
52
|
+
%w(< < ) => true,
|
53
|
+
%w(< >=) => lambda{|s, o| s > o},
|
54
|
+
%w(< <=) => true,
|
55
|
+
%w(< ~>) => lambda{|s, o| s > o},
|
56
|
+
%w(>= >=) => true,
|
57
|
+
%w(>= <=) => lambda{|s, o| s <= o},
|
58
|
+
%w(>= ~>) => lambda{|s, o| s < o.bump},
|
59
|
+
%w(<= <=) => true,
|
60
|
+
%w(<= ~>) => lambda{|s, o| s >= o},
|
61
|
+
%w(~> ~>) => lambda{|s, o| s < o.bump && s.bump > o},
|
62
|
+
}
|
63
|
+
|
64
|
+
def consistent_with?(other)
|
65
|
+
sgreq, ogreq = to_gem_requirement, other.to_gem_requirement
|
66
|
+
sreqs, oreqs = sgreq.requirements, ogreq.requirements
|
67
|
+
sreqs.all? do |sreq|
|
68
|
+
oreqs.all? do |oreq|
|
69
|
+
compatible?(sreq, oreq)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def inconsistent_with?(other)
|
75
|
+
!consistent_with?(other)
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
attr_accessor :backing
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def initialize_normalize_args(args)
|
85
|
+
args.map do |arg|
|
86
|
+
arg = arg.backing if self.class === arg
|
87
|
+
arg
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def compatible?(a, b)
|
92
|
+
a, b = b, a unless COMPATS_TABLE.include?([a.first, b.first])
|
93
|
+
r = COMPATS_TABLE[[a.first, b.first]]
|
94
|
+
r = r.call(a.last, b.last) if r.respond_to?(:call)
|
95
|
+
r
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
attr_accessor :name, :requirement, :source
|
100
|
+
private :name=, :requirement=, :source=
|
101
|
+
|
102
|
+
def initialize(name, requirement, source)
|
103
|
+
assert_name_valid! name
|
104
|
+
|
105
|
+
self.name = name
|
106
|
+
self.requirement = Requirement.new(requirement)
|
107
|
+
self.source = source
|
108
|
+
|
109
|
+
@manifests = nil
|
110
|
+
end
|
111
|
+
|
112
|
+
def manifests
|
113
|
+
@manifests ||= cache_manifests!
|
114
|
+
end
|
115
|
+
|
116
|
+
def cache_manifests!
|
117
|
+
source.manifests(name)
|
118
|
+
end
|
119
|
+
|
120
|
+
def satisfied_by?(manifest)
|
121
|
+
manifest.satisfies?(self)
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_s
|
125
|
+
"#{name} (#{requirement}) <#{source}>"
|
126
|
+
end
|
127
|
+
|
128
|
+
def ==(other)
|
129
|
+
!other.nil? &&
|
130
|
+
self.class == other.class &&
|
131
|
+
self.name == other.name &&
|
132
|
+
self.requirement == other.requirement &&
|
133
|
+
self.source == other.source
|
134
|
+
end
|
135
|
+
|
136
|
+
def consistent_with?(other)
|
137
|
+
name != other.name || requirement.consistent_with?(other.requirement)
|
138
|
+
end
|
139
|
+
|
140
|
+
def inconsistent_with?(other)
|
141
|
+
!consistent_with?(other)
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def assert_name_valid!(name)
|
147
|
+
name =~ /\A\S(?:.*\S)?\z/ and return
|
148
|
+
|
149
|
+
raise ArgumentError, "name (#{name.inspect}) must be sensible"
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module Librarian
|
4
|
+
class Dsl
|
5
|
+
class Receiver
|
6
|
+
|
7
|
+
def initialize(target)
|
8
|
+
singleton_class = class << self; self end
|
9
|
+
singleton_class.class_eval do
|
10
|
+
define_method(target.dependency_name) do |*args, &block|
|
11
|
+
target.dependency(*args, &block)
|
12
|
+
end
|
13
|
+
define_method(:source) do |*args, &block|
|
14
|
+
target.source(*args, &block)
|
15
|
+
end
|
16
|
+
target.source_types.each do |source_type|
|
17
|
+
name = source_type[0]
|
18
|
+
define_method(name) do |*args, &block|
|
19
|
+
target.source(name, *args, &block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def run(specfile = nil)
|
26
|
+
specfile = Proc.new if block_given?
|
27
|
+
|
28
|
+
case specfile
|
29
|
+
when Pathname
|
30
|
+
instance_eval(File.read(specfile), specfile.to_s, 1)
|
31
|
+
when String
|
32
|
+
instance_eval(specfile)
|
33
|
+
when Proc
|
34
|
+
instance_eval(&specfile)
|
35
|
+
else
|
36
|
+
raise ArgumentError, "specfile must be a #{Pathname}, #{String}, or #{Proc} if no block is given (it was #{specfile.inspect})"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'librarian/spec'
|
2
|
+
|
3
|
+
module Librarian
|
4
|
+
class Dsl
|
5
|
+
class Target
|
6
|
+
|
7
|
+
class SourceShortcutDefinitionReceiver
|
8
|
+
def initialize(target)
|
9
|
+
singleton_class = class << self; self end
|
10
|
+
singleton_class.class_eval do
|
11
|
+
define_method(:source) do |options|
|
12
|
+
target.source_from_options(options)
|
13
|
+
end
|
14
|
+
target.source_types.each do |source_type|
|
15
|
+
name = source_type[0]
|
16
|
+
define_method(name) do |*args|
|
17
|
+
args.push({}) unless Hash === args.last
|
18
|
+
target.source_from_params(name, *args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
SCOPABLES = [:source, :sources]
|
26
|
+
|
27
|
+
attr_accessor :dsl
|
28
|
+
private :dsl=
|
29
|
+
|
30
|
+
attr_reader :dependency_name, :dependency_type
|
31
|
+
attr_reader :source_types, :source_types_map, :source_types_reverse_map, :source_type_names, :source_shortcuts
|
32
|
+
attr_reader :dependencies, :source_cache, *SCOPABLES
|
33
|
+
|
34
|
+
def initialize(dsl)
|
35
|
+
self.dsl = dsl
|
36
|
+
@dependency_name = dsl.dependency_name
|
37
|
+
@dependency_type = dsl.dependency_type
|
38
|
+
@source_types = dsl.source_types
|
39
|
+
@source_types_map = Hash[source_types]
|
40
|
+
@source_types_reverse_map = Hash[source_types.map{|pair| a, b = pair ; [b, a]}]
|
41
|
+
@source_type_names = source_types.map{|t| t[0]}
|
42
|
+
@source_cache = {}
|
43
|
+
@source_shortcuts = {}
|
44
|
+
@dependencies = []
|
45
|
+
SCOPABLES.each do |scopable|
|
46
|
+
instance_variable_set(:"@#{scopable}", [])
|
47
|
+
end
|
48
|
+
dsl.source_shortcuts.each do |name, param|
|
49
|
+
define_source_shortcut(name, param)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_spec
|
54
|
+
Spec.new(@sources, @dependencies)
|
55
|
+
end
|
56
|
+
|
57
|
+
def dependency(name, *args)
|
58
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
59
|
+
source = source_from_options(options) || @source
|
60
|
+
dep = dependency_type.new(name, args, source)
|
61
|
+
@dependencies << dep
|
62
|
+
end
|
63
|
+
|
64
|
+
def source(name, param = nil, options = nil, &block)
|
65
|
+
if !(Hash === name) && [Array, Hash, Proc].any?{|c| c === param} && !options && !block
|
66
|
+
define_source_shortcut(name, param)
|
67
|
+
elsif !(Hash === name) && !param && !options
|
68
|
+
source = source_shortcuts[name]
|
69
|
+
scope_or_directive(block) do
|
70
|
+
@source = source
|
71
|
+
@sources = @sources.dup << source
|
72
|
+
end
|
73
|
+
else
|
74
|
+
name, param, options = *normalize_source_options(name, param, options || {})
|
75
|
+
source = source_from_params(name, param, options)
|
76
|
+
scope_or_directive(block) do
|
77
|
+
@source = source
|
78
|
+
@sources = @sources.dup << source
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def precache_sources(sources)
|
84
|
+
sources.each do |source|
|
85
|
+
key = [source_types_reverse_map[source.class], *source.to_spec_args]
|
86
|
+
source_cache[key] = source
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def scope
|
91
|
+
currents = { }
|
92
|
+
SCOPABLES.each do |scopable|
|
93
|
+
currents[scopable] = instance_variable_get(:"@#{scopable}").dup
|
94
|
+
end
|
95
|
+
yield
|
96
|
+
ensure
|
97
|
+
SCOPABLES.reverse.each do |scopable|
|
98
|
+
instance_variable_set(:"@#{scopable}", currents[scopable])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def scope_or_directive(scoped_block = nil)
|
103
|
+
unless scoped_block
|
104
|
+
yield
|
105
|
+
else
|
106
|
+
scope do
|
107
|
+
yield
|
108
|
+
scoped_block.call
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def normalize_source_options(name, param, options)
|
114
|
+
if name.is_a?(Hash)
|
115
|
+
extract_source_parts(name)
|
116
|
+
else
|
117
|
+
[name, param, options]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def extract_source_parts(options)
|
122
|
+
if name = source_type_names.find{|name| options.key?(name)}
|
123
|
+
options = options.dup
|
124
|
+
param = options.delete(name)
|
125
|
+
[name, param, options]
|
126
|
+
else
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def source_from_options(options)
|
132
|
+
if options[:source]
|
133
|
+
source_shortcuts[options[:source]]
|
134
|
+
elsif source_parts = extract_source_parts(options)
|
135
|
+
source_from_params(*source_parts)
|
136
|
+
else
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def source_from_params(name, param, options)
|
142
|
+
source_cache[[name, param, options]] ||= begin
|
143
|
+
type = source_types_map[name]
|
144
|
+
type.from_spec_args(environment, param, options)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def source_from_source_shortcut_definition(definition)
|
149
|
+
case definition
|
150
|
+
when Array
|
151
|
+
source_from_params(*definition)
|
152
|
+
when Hash
|
153
|
+
source_from_options(definition)
|
154
|
+
when Proc
|
155
|
+
receiver = SourceShortcutDefinitionReceiver.new(self)
|
156
|
+
receiver.instance_eval(&definition)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def define_source_shortcut(name, definition)
|
161
|
+
source = source_from_source_shortcut_definition(definition)
|
162
|
+
source_shortcuts[name] = source
|
163
|
+
end
|
164
|
+
|
165
|
+
def environment
|
166
|
+
dsl.environment
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'librarian/dependency'
|
2
|
+
require 'librarian/dsl/receiver'
|
3
|
+
require 'librarian/dsl/target'
|
4
|
+
|
5
|
+
module Librarian
|
6
|
+
class Dsl
|
7
|
+
|
8
|
+
class Error < Exception
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_accessor :environment
|
12
|
+
private :environment=
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
def run(environment, specfile = nil, precache_sources = [], &block)
|
17
|
+
new(environment).run(specfile, precache_sources, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def dependency(name)
|
23
|
+
dependency_name = name
|
24
|
+
dependency_type = Dependency
|
25
|
+
singleton_class = class << self; self end
|
26
|
+
singleton_class.instance_eval do
|
27
|
+
define_method(:dependency_name) { dependency_name }
|
28
|
+
define_method(:dependency_type) { dependency_type }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
define_method(:source_types) { [] }
|
33
|
+
|
34
|
+
def source(options)
|
35
|
+
name = options.keys.first
|
36
|
+
type = options[name]
|
37
|
+
types = source_types
|
38
|
+
types << [name, type]
|
39
|
+
singleton_class = class << self; self end
|
40
|
+
singleton_class.instance_eval do
|
41
|
+
define_method(:source_types) { types }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
define_method(:source_shortcuts) { {} }
|
46
|
+
|
47
|
+
def shortcut(name, options)
|
48
|
+
instances = source_shortcuts
|
49
|
+
instances[name] = options
|
50
|
+
singleton_class = class << self; self end
|
51
|
+
singleton_class.instance_eval do
|
52
|
+
define_method(:source_shortcuts) { instances }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def delegate_to_class(*names)
|
57
|
+
names.each do |name|
|
58
|
+
define_method(name) { self.class.send(name) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
delegate_to_class :dependency_name, :dependency_type, :source_types, :source_shortcuts
|
65
|
+
|
66
|
+
def initialize(environment)
|
67
|
+
self.environment = environment
|
68
|
+
end
|
69
|
+
|
70
|
+
def run(specfile = nil, sources = [])
|
71
|
+
specfile, sources = nil, specfile if specfile.kind_of?(Array) && sources.empty?
|
72
|
+
|
73
|
+
Target.new(self).tap do |target|
|
74
|
+
target.precache_sources(sources)
|
75
|
+
debug_named_source_cache("Pre-Cached Sources", target)
|
76
|
+
|
77
|
+
specfile ||= Proc.new if block_given?
|
78
|
+
receiver = Receiver.new(target)
|
79
|
+
receiver.run(specfile)
|
80
|
+
|
81
|
+
debug_named_source_cache("Post-Cached Sources", target)
|
82
|
+
end.to_spec
|
83
|
+
end
|
84
|
+
|
85
|
+
def debug_named_source_cache(name, target)
|
86
|
+
source_cache = target.source_cache
|
87
|
+
debug { "#{name}:" }
|
88
|
+
source_cache.each do |key, value|
|
89
|
+
type = key[0]
|
90
|
+
attributes = key[1...key.size]
|
91
|
+
debug { " #{key.inspect}" }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def debug(*args, &block)
|
98
|
+
environment.logger.debug(*args, &block)
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "librarian/error"
|
2
|
+
|
3
|
+
module Librarian
|
4
|
+
class Environment
|
5
|
+
class RuntimeCache
|
6
|
+
|
7
|
+
class KeyspaceCache
|
8
|
+
|
9
|
+
class << self
|
10
|
+
private
|
11
|
+
|
12
|
+
def delegate_to_backing_cache(*methods)
|
13
|
+
methods.each do |method|
|
14
|
+
define_method "#{method}" do |*args, &block|
|
15
|
+
# TODO: When we drop ruby-1.8.7 support, use #public_send.
|
16
|
+
runtime_cache.send(method, keyspace, *args, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :runtime_cache, :keyspace
|
23
|
+
|
24
|
+
def initialize(runtime_cache, keyspace)
|
25
|
+
self.runtime_cache = runtime_cache
|
26
|
+
self.keyspace = keyspace
|
27
|
+
end
|
28
|
+
|
29
|
+
delegate_to_backing_cache *[
|
30
|
+
:include?,
|
31
|
+
:get,
|
32
|
+
:put,
|
33
|
+
:delete,
|
34
|
+
:memo,
|
35
|
+
:once,
|
36
|
+
:[],
|
37
|
+
:[]=,
|
38
|
+
]
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_writer :runtime_cache, :keyspace
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize
|
47
|
+
self.data = {}
|
48
|
+
end
|
49
|
+
|
50
|
+
def include?(keyspace, key)
|
51
|
+
data.include?(combined_key(keyspace, key))
|
52
|
+
end
|
53
|
+
|
54
|
+
def get(keyspace, key)
|
55
|
+
data[combined_key(keyspace, key)]
|
56
|
+
end
|
57
|
+
|
58
|
+
def put(keyspace, key, value = nil)
|
59
|
+
data[combined_key(keyspace, key)] = block_given? ? yield : value
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete(keyspace, key)
|
63
|
+
data.delete(combined_key(keyspace, key))
|
64
|
+
end
|
65
|
+
|
66
|
+
def memo(keyspace, key)
|
67
|
+
put(keyspace, key, yield) unless include?(keyspace, key)
|
68
|
+
get(keyspace, key)
|
69
|
+
end
|
70
|
+
|
71
|
+
def once(keyspace, key)
|
72
|
+
memo(keyspace, key) { yield ; nil }
|
73
|
+
end
|
74
|
+
|
75
|
+
def [](keyspace, key)
|
76
|
+
get(keyspace, key)
|
77
|
+
end
|
78
|
+
|
79
|
+
def []=(keyspace, key, value)
|
80
|
+
put(keyspace, key, value)
|
81
|
+
end
|
82
|
+
|
83
|
+
def keyspace(keyspace)
|
84
|
+
KeyspaceCache.new(self, keyspace)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
attr_accessor :data
|
90
|
+
|
91
|
+
def combined_key(keyspace, key)
|
92
|
+
keyspace.kind_of?(String) or raise Error, "keyspace must be a string"
|
93
|
+
keyspace.size > 0 or raise Error, "keyspace must not be empty"
|
94
|
+
keyspace.size < 2**16 or raise Error, "keyspace must not be too large"
|
95
|
+
key.kind_of?(String) or raise Error, "key must be a string"
|
96
|
+
[keyspace.size.to_s(16).rjust(4, "0"), keyspace, key].join
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|