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.
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