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,225 @@
1
+ require 'thor'
2
+ require 'thor/actions'
3
+
4
+ require 'librarian'
5
+ require 'librarian/error'
6
+ require 'librarian/action'
7
+ require "librarian/ui"
8
+
9
+ module Librarian
10
+ class Cli < Thor
11
+
12
+ autoload :ManifestPresenter, "librarian/cli/manifest_presenter"
13
+
14
+ include Thor::Actions
15
+
16
+ module Particularity
17
+ def root_module
18
+ nil
19
+ end
20
+ end
21
+
22
+ extend Particularity
23
+
24
+ class << self
25
+ def bin!
26
+ status = with_environment { returning_status { start } }
27
+ exit status
28
+ end
29
+
30
+ def returning_status
31
+ yield
32
+ 0
33
+ rescue Librarian::Error => e
34
+ environment.ui.error e.message
35
+ environment.ui.debug e.backtrace.join("\n")
36
+ e.respond_to?(:status_code) && e.status_code || 1
37
+ rescue Interrupt => e
38
+ environment.ui.error "\nQuitting..."
39
+ 1
40
+ end
41
+
42
+ attr_accessor :environment
43
+
44
+ def with_environment
45
+ environment = root_module.environment_class.new
46
+ self.environment, orig_environment = environment, self.environment
47
+ yield(environment)
48
+ ensure
49
+ self.environment = orig_environment
50
+ end
51
+ end
52
+
53
+ def initialize(*)
54
+ super
55
+ the_shell = (options["no-color"] ? Thor::Shell::Basic.new : shell)
56
+ environment.ui = UI::Shell.new(the_shell)
57
+ environment.ui.be_quiet! if options["quiet"]
58
+ environment.ui.debug! if options["verbose"]
59
+ environment.ui.debug_line_numbers! if options["verbose"] && options["line-numbers"]
60
+
61
+ write_debug_header
62
+ end
63
+
64
+ desc "version", "Displays the version."
65
+ def version
66
+ say "librarian-#{environment.version}"
67
+ say "librarian-#{environment.adapter_name}-#{environment.adapter_version}"
68
+ end
69
+
70
+ desc "config", "Show or edit the config."
71
+ option "verbose", :type => :boolean, :default => false
72
+ option "line-numbers", :type => :boolean, :default => false
73
+ option "global", :type => :boolean, :default => false
74
+ option "local", :type => :boolean, :default => false
75
+ option "delete", :type => :boolean, :default => false
76
+ def config(key = nil, value = nil)
77
+ if key
78
+ raise Error, "cannot set both value and delete" if value && options["delete"]
79
+ if options["delete"]
80
+ scope = config_scope(true)
81
+ environment.config_db[key, scope] = nil
82
+ elsif value
83
+ scope = config_scope(true)
84
+ environment.config_db[key, scope] = value
85
+ else
86
+ scope = config_scope(false)
87
+ if value = environment.config_db[key, scope]
88
+ prefix = scope ? "#{key} (#{scope})" : key
89
+ say "#{prefix}: #{value}"
90
+ end
91
+ end
92
+ else
93
+ environment.config_db.keys.each do |key|
94
+ say "#{key}: #{environment.config_db[key]}"
95
+ end
96
+ end
97
+ end
98
+
99
+ desc "clean", "Cleans out the cache and install paths."
100
+ option "verbose", :type => :boolean, :default => false
101
+ option "line-numbers", :type => :boolean, :default => false
102
+ def clean
103
+ ensure!
104
+ clean!
105
+ end
106
+
107
+ desc "update", "Updates and installs the dependencies you specify."
108
+ option "verbose", :type => :boolean, :default => false
109
+ option "line-numbers", :type => :boolean, :default => false
110
+ def update(*names)
111
+ ensure!
112
+ if names.empty?
113
+ resolve!(:force => true)
114
+ else
115
+ update!(:names => names)
116
+ end
117
+ install!
118
+ end
119
+
120
+ desc "outdated", "Lists outdated dependencies."
121
+ option "verbose", :type => :boolean, :default => false
122
+ option "line-numbers", :type => :boolean, :default => false
123
+ def outdated
124
+ ensure!
125
+ resolution = environment.lock
126
+ manifests = resolution.manifests.sort_by(&:name)
127
+ manifests.select(&:outdated?).each do |manifest|
128
+ say "#{manifest.name} (#{manifest.version} -> #{manifest.latest.version})"
129
+ end
130
+ end
131
+
132
+ desc "show", "Shows dependencies"
133
+ option "verbose", :type => :boolean, :default => false
134
+ option "line-numbers", :type => :boolean, :default => false
135
+ option "detailed", :type => :boolean
136
+ def show(*names)
137
+ ensure!
138
+ if environment.lockfile_path.file?
139
+ manifest_presenter.present(names, :detailed => options["detailed"])
140
+ else
141
+ raise Error, "Be sure to install first!"
142
+ end
143
+ end
144
+
145
+ desc "init", "Initializes the current directory."
146
+ def init
147
+ puts "Nothing to do."
148
+ end
149
+
150
+ private
151
+
152
+ def environment
153
+ self.class.environment
154
+ end
155
+
156
+ def ensure!(options = { })
157
+ Action::Ensure.new(environment, options).run
158
+ end
159
+
160
+ def clean!(options = { })
161
+ Action::Clean.new(environment, options).run
162
+ end
163
+
164
+ def install!(options = { })
165
+ Action::Install.new(environment, options).run
166
+ end
167
+
168
+ def resolve!(options = { })
169
+ Action::Resolve.new(environment, options).run
170
+ end
171
+
172
+ def update!(options = { })
173
+ Action::Update.new(environment, options).run
174
+ end
175
+
176
+ def manifest_presenter
177
+ ManifestPresenter.new(self, environment.lock.manifests)
178
+ end
179
+
180
+ def write_debug_header
181
+ debug { "Ruby Version: #{RUBY_VERSION}" }
182
+ debug { "Ruby Platform: #{RUBY_PLATFORM}" }
183
+ debug { "Rubinius Version: #{Rubinius::VERSION}" } if defined?(Rubinius)
184
+ debug { "JRuby Version: #{JRUBY_VERSION}" } if defined?(JRUBY_VERSION)
185
+ debug { "Rubygems Version: #{Gem::VERSION}" }
186
+ debug { "Librarian Version: #{environment.version}" }
187
+ debug { "Librarian Adapter: #{environment.adapter_name}"}
188
+ debug { "Librarian Adapter Version: #{environment.adapter_version}" }
189
+ debug { "Project: #{environment.project_path}" }
190
+ debug { "Specfile: #{relative_path_to(environment.specfile_path)}" }
191
+ debug { "Lockfile: #{relative_path_to(environment.lockfile_path)}" }
192
+ debug { "Git: #{Source::Git::Repository.bin}" }
193
+ debug { "Git Version: #{Source::Git::Repository.git_version}" }
194
+ debug { "Git Environment Variables:" }
195
+ git_env = ENV.to_a.select{|(k, v)| k =~ /\AGIT/}.sort_by{|(k, v)| k}
196
+ if git_env.empty?
197
+ debug { " (empty)" }
198
+ else
199
+ git_env.each do |(k, v)|
200
+ debug { " #{k}=#{v}"}
201
+ end
202
+ end
203
+ end
204
+
205
+ def debug(*args, &block)
206
+ environment.logger.debug(*args, &block)
207
+ end
208
+
209
+ def relative_path_to(path)
210
+ environment.logger.relative_path_to(path)
211
+ end
212
+
213
+ def config_scope(exclusive)
214
+ g, l = "global", "local"
215
+ if exclusive
216
+ options[g] ^ options[l] or raise Error, "must set either #{g} or #{l}"
217
+ else
218
+ options[g] && options[l] and raise Error, "cannot set both #{g} and #{l}"
219
+ end
220
+
221
+ options[g] ? :global : options[l] ? :local : nil
222
+ end
223
+
224
+ end
225
+ end
@@ -0,0 +1,205 @@
1
+ require "pathname"
2
+
3
+ require "librarian/config/file_source"
4
+ require "librarian/config/hash_source"
5
+
6
+ module Librarian
7
+ module Config
8
+ class Database
9
+
10
+ class << self
11
+ def library
12
+ name.split("::").first.downcase
13
+ end
14
+ end
15
+
16
+ attr_accessor :adapter_name
17
+ private :adapter_name=
18
+
19
+ attr_accessor :root, :assigned_specfile_name
20
+ private :root=, :assigned_specfile_name=
21
+
22
+ attr_accessor :underlying_env, :underlying_pwd, :underlying_home
23
+ private :underlying_env=, :underlying_pwd=, :underlying_home=
24
+
25
+ def initialize(adapter_name, options = { })
26
+ self.adapter_name = adapter_name or raise ArgumentError, "must provide adapter_name"
27
+
28
+ options[:project_path] || options[:pwd] or raise ArgumentError, "must provide project_path or pwd"
29
+
30
+ self.root = options[:project_path] && Pathname(options[:project_path])
31
+ self.assigned_specfile_name = options[:specfile_name]
32
+ self.underlying_env = options[:env] or raise ArgumentError, "must provide env"
33
+ self.underlying_pwd = options[:pwd] && Pathname(options[:pwd])
34
+ self.underlying_home = options[:home] && Pathname(options[:home])
35
+ end
36
+
37
+ def global
38
+ memo(__method__) { new_file_source(global_config_path) }
39
+ end
40
+
41
+ def env
42
+ memo(__method__) { HashSource.new(adapter_name, :name => "environment", :raw => env_source_data) }
43
+ end
44
+
45
+ def local
46
+ memo(__method__) { new_file_source(local_config_path) }
47
+ end
48
+
49
+ def [](key, scope = nil)
50
+ case scope
51
+ when "local", :local then local[key]
52
+ when "env", :env then env[key]
53
+ when "global", :global then global[key]
54
+ when nil then local[key] || env[key] || global[key]
55
+ else raise Error, "bad scope"
56
+ end
57
+ end
58
+
59
+ def []=(key, scope, value)
60
+ case scope
61
+ when "local", :local then local[key] = value
62
+ when "global", :global then global[key] = value
63
+ else raise Error, "bad scope"
64
+ end
65
+ end
66
+
67
+ def keys
68
+ [local, env, global].inject([]){|a, e| a.concat(e.keys) ; a}.sort.uniq
69
+ end
70
+
71
+ def project_path
72
+ root || specfile_path.dirname
73
+ end
74
+
75
+ def specfile_path
76
+ if root
77
+ root + (assigned_specfile_name || default_specfile_name)
78
+ else
79
+ env_specfile_path || default_specfile_path
80
+ end
81
+ end
82
+
83
+ def specfile_name
84
+ specfile_path.basename.to_s
85
+ end
86
+
87
+ def lockfile_path
88
+ project_path + lockfile_name
89
+ end
90
+
91
+ def lockfile_name
92
+ "#{specfile_name}.lock"
93
+ end
94
+
95
+ private
96
+
97
+ def new_file_source(config_path)
98
+ return unless config_path
99
+
100
+ FileSource.new(adapter_name,
101
+ :config_path => config_path,
102
+ :forbidden_keys => [config_key, specfile_key]
103
+ )
104
+ end
105
+
106
+ def global_config_path
107
+ env_global_config_path || default_global_config_path
108
+ end
109
+
110
+ def env_global_config_path
111
+ memo(__method__) { env[config_key] }
112
+ end
113
+
114
+ def default_global_config_path
115
+ underlying_home && underlying_home + config_name
116
+ end
117
+
118
+ def local_config_path
119
+ root_local_config_path || env_local_config_path || default_local_config_path
120
+ end
121
+
122
+ def root_local_config_path
123
+ root && root + config_name
124
+ end
125
+
126
+ def env_specfile_path
127
+ memo(__method__) do
128
+ path = env[specfile_key]
129
+ path && Pathname(path)
130
+ end
131
+ end
132
+
133
+ def default_specfile_path
134
+ default_project_root_path + (assigned_specfile_name || default_specfile_name)
135
+ end
136
+
137
+ def env_local_config_path
138
+ return unless env_specfile_path
139
+
140
+ env_specfile_path.dirname + config_name
141
+ end
142
+
143
+ def default_local_config_path
144
+ default_project_root_path + config_name
145
+ end
146
+
147
+ def default_project_root_path
148
+ if root
149
+ root
150
+ else
151
+ path = underlying_pwd
152
+ path = path.dirname until project_root_path?(path) || path.dirname == path
153
+ project_root_path?(path) ? path : underlying_pwd
154
+ end
155
+ end
156
+
157
+ def project_root_path?(path)
158
+ File.file?(path + default_specfile_name)
159
+ end
160
+
161
+ def config_key
162
+ "config"
163
+ end
164
+
165
+ def specfile_key
166
+ "#{adapter_name}file"
167
+ end
168
+
169
+ def default_specfile_name
170
+ "#{adapter_name.capitalize}file"
171
+ end
172
+
173
+ def library
174
+ self.class.library
175
+ end
176
+
177
+ def config_name_prefix
178
+ ".#{library}"
179
+ end
180
+
181
+ def config_name
182
+ File.join(*[config_name_prefix, adapter_name, "config"])
183
+ end
184
+
185
+ def raw_key_prefix
186
+ "#{library.upcase}_#{adapter_name.upcase}_"
187
+ end
188
+
189
+ def env_source_data
190
+ prefix = raw_key_prefix
191
+
192
+ data = underlying_env.dup
193
+ data.reject!{|k, _| !k.start_with?(prefix) || k.size <= prefix.size}
194
+ data
195
+ end
196
+
197
+ def memo(key)
198
+ key = "@#{key}"
199
+ instance_variable_set(key, yield) unless instance_variable_defined?(key)
200
+ instance_variable_get(key)
201
+ end
202
+
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,47 @@
1
+ require "yaml"
2
+
3
+ require "librarian/config/source"
4
+
5
+ module Librarian
6
+ module Config
7
+ class FileSource < Source
8
+
9
+ attr_accessor :config_path
10
+ private :config_path=
11
+
12
+ def initialize(adapter_name, options = { })
13
+ super
14
+
15
+ self.config_path = options.delete(:config_path) or raise ArgumentError, "must provide config_path"
16
+ end
17
+
18
+ def to_s
19
+ config_path
20
+ end
21
+
22
+ private
23
+
24
+ def load
25
+ return { } unless File.file?(config_path)
26
+
27
+ raw = YAML.load_file(config_path)
28
+ return { } unless Hash === raw
29
+
30
+ translate_raw_to_config(raw)
31
+ end
32
+
33
+ def save(config)
34
+ raw = translate_config_to_raw(config)
35
+
36
+ if config.empty?
37
+ File.delete(config_path) if File.file?(config_path)
38
+ else
39
+ config_dir = File.dirname(config_path)
40
+ FileUtils.mkpath(config_dir) unless File.directory?(config_dir)
41
+ File.open(config_path, "wb"){|f| YAML.dump(raw, f)}
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,33 @@
1
+ require "librarian/source"
2
+
3
+ module Librarian
4
+ module Config
5
+ class HashSource < Source
6
+
7
+ attr_accessor :name, :raw
8
+ private :name=, :raw=
9
+
10
+ def initialize(adapter_name, options = { })
11
+ super
12
+
13
+ self.name = options.delete(:name) or raise ArgumentError, "must provide name"
14
+ self.raw = options.delete(:raw) or raise ArgumentError, "must provide raw"
15
+ end
16
+
17
+ def to_s
18
+ name
19
+ end
20
+
21
+ private
22
+
23
+ def load
24
+ translate_raw_to_config(raw)
25
+ end
26
+
27
+ def save(config)
28
+ raise Error, "nonsense!"
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,149 @@
1
+ require "librarian/error"
2
+
3
+ module Librarian
4
+ module Config
5
+ class Source
6
+
7
+ RAW_KEY_SUFFIX_VALIDITY_PATTERN =
8
+ /\A[A-Z0-9_]+\z/
9
+ CONFIG_KEY_VALIDITY_PATTERN =
10
+ /\A[a-z][a-z0-9\-]+(?:\.[a-z0-9\-]+)*\z/
11
+
12
+ class << self
13
+ def raw_key_suffix_validity_pattern
14
+ RAW_KEY_SUFFIX_VALIDITY_PATTERN
15
+ end
16
+ def config_key_validity_pattern
17
+ CONFIG_KEY_VALIDITY_PATTERN
18
+ end
19
+ end
20
+
21
+ attr_accessor :adapter_name
22
+ private :adapter_name=
23
+
24
+ def initialize(adapter_name, options = { })
25
+ self.adapter_name = adapter_name
26
+
27
+ self.forbidden_keys = options.delete(:forbidden_keys) || []
28
+ end
29
+
30
+ def [](key)
31
+ load!
32
+
33
+ data[key]
34
+ end
35
+
36
+ def []=(key, value)
37
+ key_permitted?(key) or raise Error, "key not permitted: #{key.inspect}"
38
+ value_permitted?(key, value) or raise Error, "value for key #{key.inspect} not permitted: #{value.inspect}"
39
+
40
+ load!
41
+ if value.nil?
42
+ data.delete(key)
43
+ else
44
+ data[key] = value
45
+ end
46
+ save(data)
47
+ end
48
+
49
+ def keys
50
+ load!
51
+
52
+ data.keys
53
+ end
54
+
55
+ private
56
+
57
+ attr_accessor :data, :forbidden_keys
58
+
59
+ def load!
60
+ self.data = load unless data
61
+ end
62
+
63
+ def key_permitted?(key)
64
+ String === key &&
65
+ config_key_validity_pattern === key &&
66
+ !forbidden_keys.any?{|k| k === key}
67
+ end
68
+
69
+ def value_permitted?(key, value)
70
+ return true if value.nil?
71
+
72
+ String === value
73
+ end
74
+
75
+ def raw_key_valid?(key)
76
+ return false unless key.start_with?(raw_key_prefix)
77
+
78
+ suffix = key[raw_key_prefix.size..-1]
79
+ raw_key_suffix_validity_pattern =~ suffix
80
+ end
81
+
82
+ def raw_key_suffix_validity_pattern
83
+ self.class.raw_key_suffix_validity_pattern
84
+ end
85
+
86
+ def config_key_valid?(key)
87
+ config_key_validity_pattern === key
88
+ end
89
+
90
+ def config_key_validity_pattern
91
+ self.class.config_key_validity_pattern
92
+ end
93
+
94
+ def raw_key_prefix
95
+ @key_prefix ||= "LIBRARIAN_#{adapter_name.upcase}_"
96
+ end
97
+
98
+ def assert_raw_keys_valid!(raw)
99
+ bad_keys = raw.keys.reject{|k| raw_key_valid?(k)}
100
+ unless bad_keys.empty?
101
+ config_path_s = config_path.to_s.inspect
102
+ bad_keys_s = bad_keys.map(&:inspect).join(", ")
103
+ raise Error, "config #{to_s} has bad keys: #{bad_keys_s}"
104
+ end
105
+ end
106
+
107
+ def assert_config_keys_valid!(config)
108
+ bad_keys = config.keys.reject{|k| config_key_valid?(k)}
109
+ unless bad_keys.empty?
110
+ bad_keys_s = bad_keys.map(&:inspect).join(", ")
111
+ raise Error, "config has bad keys: #{bad_keys_s}"
112
+ end
113
+ end
114
+
115
+ def assert_values_valid!(data)
116
+ bad_data = data.reject{|k, v| String === v}
117
+ bad_keys = bad_data.keys
118
+
119
+ unless bad_keys.empty?
120
+ bad_keys_s = bad_keys.map(&:inspect).join(", ")
121
+ raise Error, "config has bad values for keys: #{bad_keys_s}"
122
+ end
123
+ end
124
+
125
+ def translate_raw_to_config(raw)
126
+ assert_raw_keys_valid!(raw)
127
+ assert_values_valid!(raw)
128
+
129
+ Hash[raw.map do |key, value|
130
+ key = key[raw_key_prefix.size .. -1]
131
+ key = key.downcase.gsub(/__/, ".").gsub(/_/, "-")
132
+ [key, value]
133
+ end]
134
+ end
135
+
136
+ def translate_config_to_raw(config)
137
+ assert_config_keys_valid!(config)
138
+ assert_values_valid!(config)
139
+
140
+ Hash[config.map do |key, value|
141
+ key = key.gsub(/\./, "__").gsub(/\-/, "_").upcase
142
+ key = "#{raw_key_prefix}#{key}"
143
+ [key, value]
144
+ end]
145
+ end
146
+
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,7 @@
1
+ require "librarian/config/database"
2
+
3
+ module Librarian
4
+ module Config
5
+
6
+ end
7
+ end