librarianp 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +10 -0
  5. data/CHANGELOG.md +255 -0
  6. data/Gemfile +8 -0
  7. data/Gemfile.lock +235 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +55 -0
  10. data/Rakefile +28 -0
  11. data/VERSION +1 -0
  12. data/lib/librarian/action/base.rb +24 -0
  13. data/lib/librarian/action/clean.rb +44 -0
  14. data/lib/librarian/action/ensure.rb +24 -0
  15. data/lib/librarian/action/install.rb +95 -0
  16. data/lib/librarian/action/persist_resolution_mixin.rb +51 -0
  17. data/lib/librarian/action/resolve.rb +46 -0
  18. data/lib/librarian/action/update.rb +44 -0
  19. data/lib/librarian/action.rb +5 -0
  20. data/lib/librarian/algorithms.rb +133 -0
  21. data/lib/librarian/cli/manifest_presenter.rb +89 -0
  22. data/lib/librarian/cli.rb +225 -0
  23. data/lib/librarian/config/database.rb +205 -0
  24. data/lib/librarian/config/file_source.rb +47 -0
  25. data/lib/librarian/config/hash_source.rb +33 -0
  26. data/lib/librarian/config/source.rb +149 -0
  27. data/lib/librarian/config.rb +7 -0
  28. data/lib/librarian/dependency.rb +153 -0
  29. data/lib/librarian/dsl/receiver.rb +42 -0
  30. data/lib/librarian/dsl/target.rb +171 -0
  31. data/lib/librarian/dsl.rb +102 -0
  32. data/lib/librarian/environment/runtime_cache.rb +101 -0
  33. data/lib/librarian/environment.rb +230 -0
  34. data/lib/librarian/error.rb +4 -0
  35. data/lib/librarian/helpers.rb +29 -0
  36. data/lib/librarian/linter/source_linter.rb +55 -0
  37. data/lib/librarian/lockfile/compiler.rb +66 -0
  38. data/lib/librarian/lockfile/parser.rb +123 -0
  39. data/lib/librarian/lockfile.rb +29 -0
  40. data/lib/librarian/logger.rb +46 -0
  41. data/lib/librarian/manifest.rb +146 -0
  42. data/lib/librarian/manifest_set.rb +150 -0
  43. data/lib/librarian/mock/cli.rb +19 -0
  44. data/lib/librarian/mock/dsl.rb +15 -0
  45. data/lib/librarian/mock/environment.rb +21 -0
  46. data/lib/librarian/mock/extension.rb +9 -0
  47. data/lib/librarian/mock/source/mock/registry.rb +83 -0
  48. data/lib/librarian/mock/source/mock.rb +80 -0
  49. data/lib/librarian/mock/source.rb +1 -0
  50. data/lib/librarian/mock/version.rb +5 -0
  51. data/lib/librarian/mock.rb +1 -0
  52. data/lib/librarian/posix.rb +129 -0
  53. data/lib/librarian/resolution.rb +46 -0
  54. data/lib/librarian/resolver/implementation.rb +238 -0
  55. data/lib/librarian/resolver.rb +94 -0
  56. data/lib/librarian/rspec/support/cli_macro.rb +120 -0
  57. data/lib/librarian/source/basic_api.rb +45 -0
  58. data/lib/librarian/source/git/repository.rb +193 -0
  59. data/lib/librarian/source/git.rb +172 -0
  60. data/lib/librarian/source/local.rb +54 -0
  61. data/lib/librarian/source/path.rb +56 -0
  62. data/lib/librarian/source.rb +2 -0
  63. data/lib/librarian/spec.rb +13 -0
  64. data/lib/librarian/spec_change_set.rb +173 -0
  65. data/lib/librarian/specfile.rb +19 -0
  66. data/lib/librarian/support/abstract_method.rb +21 -0
  67. data/lib/librarian/ui.rb +64 -0
  68. data/lib/librarian/version.rb +3 -0
  69. data/lib/librarian.rb +11 -0
  70. data/librarian.gemspec +47 -0
  71. data/spec/functional/cli_spec.rb +27 -0
  72. data/spec/functional/posix_spec.rb +32 -0
  73. data/spec/functional/source/git/repository_spec.rb +199 -0
  74. data/spec/functional/source/git_spec.rb +174 -0
  75. data/spec/support/fakefs.rb +37 -0
  76. data/spec/support/method_patch_macro.rb +30 -0
  77. data/spec/support/project_path_macro.rb +14 -0
  78. data/spec/support/with_env_macro.rb +22 -0
  79. data/spec/unit/action/base_spec.rb +18 -0
  80. data/spec/unit/action/clean_spec.rb +102 -0
  81. data/spec/unit/action/ensure_spec.rb +37 -0
  82. data/spec/unit/action/install_spec.rb +111 -0
  83. data/spec/unit/algorithms_spec.rb +131 -0
  84. data/spec/unit/config/database_spec.rb +320 -0
  85. data/spec/unit/dependency/requirement_spec.rb +12 -0
  86. data/spec/unit/dependency_spec.rb +212 -0
  87. data/spec/unit/dsl_spec.rb +173 -0
  88. data/spec/unit/environment/runtime_cache_spec.rb +73 -0
  89. data/spec/unit/environment_spec.rb +209 -0
  90. data/spec/unit/lockfile/parser_spec.rb +162 -0
  91. data/spec/unit/lockfile_spec.rb +65 -0
  92. data/spec/unit/manifest/version_spec.rb +11 -0
  93. data/spec/unit/manifest_set_spec.rb +202 -0
  94. data/spec/unit/manifest_spec.rb +36 -0
  95. data/spec/unit/mock/environment_spec.rb +25 -0
  96. data/spec/unit/mock/source/mock_spec.rb +22 -0
  97. data/spec/unit/resolver_spec.rb +299 -0
  98. data/spec/unit/source/git_spec.rb +29 -0
  99. data/spec/unit/spec_change_set_spec.rb +169 -0
  100. 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