librarian 0.0.1

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 (65) hide show
  1. data/.gitignore +5 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +46 -0
  6. data/Rakefile +13 -0
  7. data/bin/librarian-chef +7 -0
  8. data/bin/librarian-mock +7 -0
  9. data/config/cucumber.yaml +1 -0
  10. data/features/chef/cli/install.feature +69 -0
  11. data/features/support/env.rb +5 -0
  12. data/lib/librarian.rb +191 -0
  13. data/lib/librarian/chef.rb +1 -0
  14. data/lib/librarian/chef/cli.rb +14 -0
  15. data/lib/librarian/chef/dsl.rb +14 -0
  16. data/lib/librarian/chef/extension.rb +24 -0
  17. data/lib/librarian/chef/manifest.rb +43 -0
  18. data/lib/librarian/chef/particularity.rb +9 -0
  19. data/lib/librarian/chef/source.rb +3 -0
  20. data/lib/librarian/chef/source/git.rb +14 -0
  21. data/lib/librarian/chef/source/local.rb +80 -0
  22. data/lib/librarian/chef/source/path.rb +14 -0
  23. data/lib/librarian/chef/source/site.rb +271 -0
  24. data/lib/librarian/cli.rb +76 -0
  25. data/lib/librarian/dependency.rb +44 -0
  26. data/lib/librarian/dsl.rb +76 -0
  27. data/lib/librarian/dsl/receiver.rb +46 -0
  28. data/lib/librarian/dsl/target.rb +164 -0
  29. data/lib/librarian/helpers.rb +13 -0
  30. data/lib/librarian/helpers/debug.rb +35 -0
  31. data/lib/librarian/lockfile.rb +31 -0
  32. data/lib/librarian/lockfile/compiler.rb +69 -0
  33. data/lib/librarian/lockfile/parser.rb +102 -0
  34. data/lib/librarian/manifest.rb +88 -0
  35. data/lib/librarian/manifest_set.rb +131 -0
  36. data/lib/librarian/mock.rb +1 -0
  37. data/lib/librarian/mock/cli.rb +14 -0
  38. data/lib/librarian/mock/dsl.rb +14 -0
  39. data/lib/librarian/mock/extension.rb +28 -0
  40. data/lib/librarian/mock/particularity.rb +7 -0
  41. data/lib/librarian/mock/source.rb +1 -0
  42. data/lib/librarian/mock/source/mock.rb +88 -0
  43. data/lib/librarian/mock/source/mock/registry.rb +79 -0
  44. data/lib/librarian/particularity.rb +7 -0
  45. data/lib/librarian/resolution.rb +36 -0
  46. data/lib/librarian/resolver.rb +139 -0
  47. data/lib/librarian/source.rb +2 -0
  48. data/lib/librarian/source/git.rb +91 -0
  49. data/lib/librarian/source/git/repository.rb +82 -0
  50. data/lib/librarian/source/local.rb +33 -0
  51. data/lib/librarian/source/path.rb +52 -0
  52. data/lib/librarian/spec.rb +11 -0
  53. data/lib/librarian/spec_change_set.rb +169 -0
  54. data/lib/librarian/specfile.rb +16 -0
  55. data/lib/librarian/support/abstract_method.rb +21 -0
  56. data/lib/librarian/ui.rb +64 -0
  57. data/lib/librarian/version.rb +3 -0
  58. data/librarian.gemspec +29 -0
  59. data/spec/chef/git_source_spec.rb +93 -0
  60. data/spec/dsl_spec.rb +167 -0
  61. data/spec/lockfile_spec.rb +44 -0
  62. data/spec/meta/requires_spec.rb +27 -0
  63. data/spec/resolver_spec.rb +172 -0
  64. data/spec/spec_change_set_spec.rb +165 -0
  65. metadata +172 -0
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+
3
+ module Librarian
4
+ class Dependency
5
+
6
+ class Requirement < Gem::Requirement
7
+ end
8
+
9
+ attr_reader :name, :requirement, :source
10
+
11
+ def initialize(name, requirement, source)
12
+ @name = name
13
+ @requirement = Requirement.create(requirement)
14
+ @source = source
15
+ @manifests = nil
16
+ end
17
+
18
+ def manifests
19
+ @manifests ||= cache_manifests!
20
+ end
21
+
22
+ def cache_manifests!
23
+ source.cache!([self])
24
+ source.manifests(self)
25
+ end
26
+
27
+ def satisfied_by?(manifest)
28
+ manifest.satisfies?(self)
29
+ end
30
+
31
+ def to_s
32
+ "#{name} (#{requirement}) <#{source}>"
33
+ end
34
+
35
+ def ==(other)
36
+ !other.nil? &&
37
+ self.class == other.class &&
38
+ self.name == other.name &&
39
+ self.requirement == other.requirement &&
40
+ self.source == other.source
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,76 @@
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
+ class << self
12
+
13
+ def run(specfile = nil, precache_sources = [], &block)
14
+ new.run(specfile, precache_sources, &block)
15
+ end
16
+
17
+ private
18
+
19
+ def dependency(name)
20
+ dependency_name = name
21
+ dependency_type = Dependency
22
+ singleton_class = class << self; self end
23
+ singleton_class.instance_eval do
24
+ define_method(:dependency_name) { dependency_name }
25
+ define_method(:dependency_type) { dependency_type }
26
+ end
27
+ end
28
+
29
+ define_method(:source_types) { [] }
30
+
31
+ def source(options)
32
+ name = options.keys.first
33
+ type = options[name]
34
+ types = source_types
35
+ types << [name, type]
36
+ singleton_class = class << self; self end
37
+ singleton_class.instance_eval do
38
+ define_method(:source_types) { types }
39
+ end
40
+ end
41
+
42
+ define_method(:source_shortcuts) { {} }
43
+
44
+ def shortcut(name, options)
45
+ instances = source_shortcuts
46
+ instances[name] = options
47
+ singleton_class = class << self; self end
48
+ singleton_class.instance_eval do
49
+ define_method(:source_shortcuts) { instances }
50
+ end
51
+ end
52
+
53
+ def delegate_to_class(*names)
54
+ names.each do |name|
55
+ define_method(name) { self.class.send(name) }
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ delegate_to_class :dependency_name, :dependency_type, :source_types, :source_shortcuts
62
+
63
+ def run(specfile = nil, sources = [])
64
+ Target.new(self).tap do |target|
65
+ target.precache_sources(sources)
66
+ receiver = Receiver.new(target)
67
+ if block_given?
68
+ receiver.run(&Proc.new)
69
+ else
70
+ receiver.run(specfile)
71
+ end
72
+ end.to_spec
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,46 @@
1
+ require 'librarian/specfile'
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
+ if block_given?
27
+ instance_eval(&Proc.new)
28
+ else
29
+ case specfile
30
+ when Specfile
31
+ eval(specfile.path.read, instance_binding, specfile.path.to_s, 1)
32
+ when String
33
+ eval(specfile, instance_binding)
34
+ when Proc
35
+ instance_eval(&specfile)
36
+ end
37
+ end
38
+ end
39
+
40
+ def instance_binding
41
+ binding
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,164 @@
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 = [:sources]
26
+
27
+ attr_reader :dependency_name, :dependency_type
28
+ attr_reader :source_types, :source_types_map, :source_types_reverse_map, :source_type_names, :source_shortcuts
29
+ attr_reader :dependencies, :source_cache, *SCOPABLES
30
+
31
+ def initialize(dsl)
32
+ @dependency_name = dsl.dependency_name
33
+ @dependency_type = dsl.dependency_type
34
+ @source_types = dsl.source_types
35
+ @source_types_map = Hash[source_types]
36
+ @source_types_reverse_map = Hash[source_types.map{|pair| a, b = pair ; [b, a]}]
37
+ @source_type_names = source_types.map{|t| t[0]}
38
+ @source_cache = {}
39
+ @source_shortcuts = {}
40
+ @dependencies = []
41
+ SCOPABLES.each do |scopable|
42
+ instance_variable_set(:"@#{scopable}", [])
43
+ end
44
+ dsl.source_shortcuts.each do |name, param|
45
+ define_source_shortcut(name, param)
46
+ end
47
+ end
48
+
49
+ def to_spec
50
+ Spec.new(@sources.first, @dependencies)
51
+ end
52
+
53
+ def dependency(name, *args)
54
+ options = args.last.is_a?(Hash) ? args.pop : {}
55
+ source = source_from_options(options) || @sources.last
56
+ unless source
57
+ raise Error, "#{dependency_name} #{name} is specified without a source!"
58
+ end
59
+ dep = dependency_type.new(name, args, source)
60
+ @dependencies << dep
61
+ end
62
+
63
+ def source(name, param = nil, options = nil, &block)
64
+ if !(Hash === name) && [Array, Hash, Proc].any?{|c| c === param} && !options && !block
65
+ define_source_shortcut(name, param)
66
+ elsif !(Hash === name) && !param && !options
67
+ source = source_shortcuts[name]
68
+ scope_or_directive(block) do
69
+ @sources = @sources.dup << source
70
+ end
71
+ else
72
+ name, param, options = *normalize_source_options(name, param, options || {})
73
+ source = source_from_params(name, param, options)
74
+ scope_or_directive(block) do
75
+ @sources = @sources.dup << source
76
+ end
77
+ end
78
+ end
79
+
80
+ def precache_sources(sources)
81
+ sources.each do |source|
82
+ key = [source_types_reverse_map[source.class], *source.to_spec_args]
83
+ source_cache[key] = source
84
+ end
85
+ end
86
+
87
+ def scope
88
+ currents = { }
89
+ SCOPABLES.each do |scopable|
90
+ currents[scopable] = instance_variable_get(:"@#{scopable}").dup
91
+ end
92
+ yield
93
+ ensure
94
+ SCOPABLES.reverse.each do |scopable|
95
+ instance_variable_set(:"@#{scopable}", currents[scopable])
96
+ end
97
+ end
98
+
99
+ def scope_or_directive(scoped_block = nil)
100
+ unless scoped_block
101
+ yield
102
+ else
103
+ scope do
104
+ yield
105
+ scoped_block.call
106
+ end
107
+ end
108
+ end
109
+
110
+ def normalize_source_options(name, param, options)
111
+ if name.is_a?(Hash)
112
+ extract_source_parts(name)
113
+ else
114
+ [name, param, options]
115
+ end
116
+ end
117
+
118
+ def extract_source_parts(options)
119
+ if name = source_type_names.find{|name| options.key?(name)}
120
+ options = options.dup
121
+ param = options.delete(name)
122
+ [name, param, options]
123
+ else
124
+ nil
125
+ end
126
+ end
127
+
128
+ def source_from_options(options)
129
+ if options[:source]
130
+ source_shortcuts[options[:source]]
131
+ elsif source_parts = extract_source_parts(options)
132
+ source_from_params(*source_parts)
133
+ else
134
+ nil
135
+ end
136
+ end
137
+
138
+ def source_from_params(name, param, options)
139
+ source_cache[[name, param, options]] ||= begin
140
+ type = source_types_map[name]
141
+ type.new(param, options)
142
+ end
143
+ end
144
+
145
+ def source_from_source_shortcut_definition(definition)
146
+ case definition
147
+ when Array
148
+ source_from_params(*definition)
149
+ when Hash
150
+ source_from_options(definition)
151
+ when Proc
152
+ receiver = SourceShortcutDefinitionReceiver.new(self)
153
+ receiver.instance_eval(&definition)
154
+ end
155
+ end
156
+
157
+ def define_source_shortcut(name, definition)
158
+ source = source_from_source_shortcut_definition(definition)
159
+ source_shortcuts[name] = source
160
+ end
161
+
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,13 @@
1
+ module Librarian
2
+ module Helpers
3
+ extend self
4
+
5
+ # [active_support/core_ext/string/strip]
6
+ def strip_heredoc(string)
7
+ indent = string.scan(/^[ \t]*(?=\S)/).min
8
+ indent = indent.respond_to?(:size) ? indent.size : 0
9
+ string.gsub(/^[ \t]{#{indent}}/, '')
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,35 @@
1
+ require 'librarian/support/abstract_method'
2
+
3
+ module Librarian
4
+ module Helpers
5
+ module Debug
6
+
7
+ include Support::AbstractMethod
8
+
9
+ LIBRARIAN_PATH = Pathname.new('../../../../').expand_path(__FILE__)
10
+
11
+ abstract_method :root_module
12
+
13
+ private
14
+
15
+ def relative_path_to(path)
16
+ root_module.project_relative_path_to(path)
17
+ end
18
+
19
+ def debug
20
+ if root_module.ui
21
+ if root_module.ui.respond_to? :debug_line_numbers and root_module.ui.debug_line_numbers
22
+ loc = caller.find{|l| !(l =~ /in `debug'$/)}
23
+ if loc =~ /^(.+):(\d+):in `(.+)'$/
24
+ loc = "#{Pathname.new($1).relative_path_from(LIBRARIAN_PATH)}:#{$2}:in `#{$3}'"
25
+ end
26
+ root_module.ui.debug { "[Librarian] #{yield} [#{loc}]" }
27
+ else
28
+ root_module.ui.debug { "[Librarian] #{yield}" }
29
+ end
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ require 'librarian/helpers/debug'
2
+
3
+ require 'librarian/lockfile/compiler'
4
+ require 'librarian/lockfile/parser'
5
+
6
+ module Librarian
7
+ class Lockfile
8
+
9
+ include Helpers::Debug
10
+
11
+ attr_reader :root_module, :path
12
+
13
+ def initialize(root_module, path)
14
+ @root_module = root_module
15
+ @path = path
16
+ end
17
+
18
+ def save(resolution)
19
+ Compiler.new(root_module).compile(resolution)
20
+ end
21
+
22
+ def load(string)
23
+ Parser.new(root_module).parse(string)
24
+ end
25
+
26
+ def read
27
+ load(path.read)
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,69 @@
1
+ require 'librarian/helpers/debug'
2
+
3
+ module Librarian
4
+ class Lockfile
5
+ class Compiler
6
+
7
+ include Helpers::Debug
8
+
9
+ attr_reader :root_module
10
+
11
+ def initialize(root_module)
12
+ @root_module = root_module
13
+ end
14
+
15
+ def compile(resolution)
16
+ out = StringIO.new
17
+ save_sources(out, resolution.manifests)
18
+ save_dependencies(out, resolution.dependencies)
19
+ out.string
20
+ end
21
+
22
+ private
23
+
24
+ def save_sources(out, manifests)
25
+ dsl_class.source_types.map{|t| t[1]}.each do |type|
26
+ type_manifests = manifests.select{|m| type === m.source}
27
+ sources = type_manifests.map{|m| m.source}.uniq.sort_by{|s| s.to_s}
28
+ sources.each do |source|
29
+ source_manifests = type_manifests.select{|m| source == m.source}
30
+ save_source(out, source, source_manifests)
31
+ end
32
+ end
33
+ end
34
+
35
+ def save_source(out, source, manifests)
36
+ out.puts "#{source.class.lock_name}"
37
+ options = source.to_lock_options
38
+ remote = options.delete(:remote)
39
+ out.puts " remote: #{remote}"
40
+ options.to_a.sort_by{|a| a[0].to_s}.each do |o|
41
+ out.puts " #{o[0]}: #{o[1]}"
42
+ end
43
+ out.puts " specs:"
44
+ manifests.sort_by{|a| a.name}.each do |manifest|
45
+ out.puts " #{manifest.name} (#{manifest.version})"
46
+ manifest.dependencies.sort_by{|a| a.name}.each do |dependency|
47
+ out.puts " #{dependency.name} (#{dependency.requirement})"
48
+ end
49
+ end
50
+ out.puts ""
51
+ end
52
+
53
+ def save_dependencies(out, dependencies)
54
+ out.puts "DEPENDENCIES"
55
+ dependencies.sort_by{|a| a.name}.each do |d|
56
+ res = "#{d.name}"
57
+ res << " (#{d.requirement})" if d.requirement
58
+ out.puts " #{res}"
59
+ end
60
+ out.puts ""
61
+ end
62
+
63
+ def dsl_class
64
+ root_module.dsl_class
65
+ end
66
+
67
+ end
68
+ end
69
+ end