dupervisor 1.0.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.
@@ -0,0 +1,60 @@
1
+ module DuperVisor
2
+ module Formats
3
+ module Accessors
4
+ def formats
5
+ ::DuperVisor::Formats::Base.formats
6
+ end
7
+ def format_classes
8
+ ::DuperVisor::Formats::Base.formats.values.map(&:to_s).sort.uniq.map{ |klass| Module.const_get(klass) }
9
+ end
10
+ end
11
+
12
+ class Base
13
+ @formats = {}
14
+ class << self
15
+ attr_writer :formats
16
+ attr_accessor :format_aliases
17
+
18
+ # Returns a hash: +{ format: format_class, format_alias: format_class, ...}+
19
+ # For example { yaml: YAML, yml: YAML, etc.}
20
+ def formats
21
+ aliases = {}
22
+ @formats.values.each do |format_class|
23
+ if format_class.aliases && !format_class.aliases.empty?
24
+ format_class.aliases.each { |format_alias| aliases[format_alias] = format_class }
25
+ end
26
+ end
27
+ @formats.merge!(aliases)
28
+ end
29
+ end
30
+
31
+ def self.inherited(klass)
32
+ klass.instance_eval do
33
+ class << self
34
+ %i(aliases from to errors).each do |a|
35
+ define_method(a) { |*args|
36
+ arg = args.first
37
+ %i(aliases errors).each do |array_field|
38
+ if a == array_field && arg && !arg.is_a?(Array)
39
+ arg = [arg]
40
+ end
41
+ end
42
+ arg ?
43
+ instance_variable_set("@#{a}".to_sym, arg) :
44
+ instance_variable_get("@#{a}".to_sym)
45
+ }
46
+ end
47
+ end
48
+
49
+ def format
50
+ self.name.split(/::/)[-1].downcase.to_sym
51
+ end
52
+ end
53
+
54
+ self.formats[klass.format] = klass
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ DuperVisor.dir('dupervisor/formats')
@@ -0,0 +1,45 @@
1
+ require 'inifile'
2
+ require_relative '../formats'
3
+ module DuperVisor
4
+ module Formats
5
+ class Ini < Base
6
+ def self.transform_hash
7
+ delete_keys = ::Set.new
8
+ keys_to_add = {}
9
+ if block_given?
10
+ hash = yield(keys_to_add, delete_keys)
11
+ end
12
+ delete_keys.each { |k| hash.delete(k) }
13
+ hash.merge!(keys_to_add)
14
+ end
15
+
16
+ errors [IniFile::Error]
17
+
18
+ from ->(string) do
19
+ hash = IniFile.new(content: string).to_h
20
+ Ini.transform_hash do |keys_to_add, delete_keys|
21
+ hash.keys.select { |k| hash[k].is_a?(Hash) && k =~ /.*:.*/ }.each do |key|
22
+ outer, inner = key.split(/:/)
23
+ keys_to_add[outer] ||= {}
24
+ keys_to_add[outer][inner] = hash[key]
25
+ delete_keys << key
26
+ end
27
+ hash
28
+ end
29
+ end
30
+
31
+ to ->(hash) do
32
+ Ini.transform_hash do |keys_to_add, delete_keys|
33
+ hash.keys.select { |k| hash[k].is_a?(Hash) }.each do |key|
34
+ hash[key].keys.select { |k| hash[key][k].is_a?(Hash) }.each do |sub_key|
35
+ keys_to_add["#{key}:#{sub_key}"] = hash[key][sub_key]
36
+ delete_keys << key
37
+ end
38
+ end
39
+ hash
40
+ end
41
+ IniFile.new.merge!(hash).to_s
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,11 @@
1
+ require 'json'
2
+ require_relative '../formats'
3
+ module DuperVisor
4
+ module Formats
5
+ class JSON < Base
6
+ from ->(string) { ::JSON.parse(string) }
7
+ to ->(hash) { ::JSON.pretty_generate(hash) }
8
+ errors [::JSON::ParserError]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ require 'yaml'
2
+ require_relative '../formats'
3
+
4
+ module DuperVisor
5
+ module Formats
6
+ class YAML < Base
7
+ aliases %i(yml)
8
+ from ->(string) { ::YAML.load(string) }
9
+ to ->(hash) { ::YAML.dump(hash) }
10
+ errors [Psych::SyntaxError]
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'renderer'
2
+ require_relative 'detector'
3
+ require_relative 'parser'
4
+ require_relative 'content'
5
+ require 'colored2'
6
+ module DuperVisor
7
+ class Main
8
+ attr_accessor :config, :from_format, :content
9
+
10
+ def initialize(config)
11
+ self.config = config
12
+ end
13
+
14
+ def run
15
+ config.validate!
16
+ self.from_format = Detector.new(ARGF.filename).detect
17
+ self.content = Parser.new(ARGF.read).parse(from_format)
18
+ Renderer.new(content.parse_result, config.output).render(config.to)
19
+ rescue DuperVisor::Parser::ParseError => e
20
+ puts ' Error:'.bold.white + ' Unable to parse input.'.bold.red
21
+ puts 'Details:'.bold.white + " #{e.inspect}".red
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ require_relative 'formats'
2
+ module DuperVisor
3
+ #
4
+ # This class is responsible for coordination of parsing attempts of a
5
+ # given contet with diffirent format parsers.
6
+ #
7
+ class Parser
8
+ class ParseError < StandardError; end
9
+ class FormatNotFound < ParseError; end
10
+
11
+ include DuperVisor::Formats::Accessors
12
+
13
+ attr_accessor :content, :parse_errors
14
+
15
+ def initialize(body)
16
+ self.content = Content.new(body: body)
17
+ end
18
+
19
+ def parse(input_format = nil)
20
+ self.parse_errors = {}
21
+ content.parse_result = nil
22
+ formats_to_check = input_format ? [ formats[input_format] ] : format_classes
23
+ formats_to_check.each do |format_class|
24
+ format = format_class.format
25
+ parse_result = try_format(format, format_class)
26
+ if parse_result
27
+ content.parse_result = parse_result
28
+ content.format = format
29
+ break
30
+ end
31
+ end
32
+ unless content.parse_result || parse_errors.empty?
33
+ if input_format
34
+ raise ParseError.new("Can not parse from format #{input_format}. Format exception:" +
35
+ parse_errors.values.map(&:inspect).join("\n"))
36
+ else
37
+ raise FormatNotFound.new('No suitable format detected. Query #parse_errors for specifics.')
38
+ end
39
+ end
40
+ content
41
+ end
42
+
43
+ def try_format(format, format_class)
44
+ format_class.from.call(content.body)
45
+ rescue *(format_class.errors) => e
46
+ self.parse_errors[format] = e
47
+ nil
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,24 @@
1
+ require 'set'
2
+ require_relative 'formats'
3
+ module DuperVisor
4
+ class Renderer
5
+ include DuperVisor::Formats::Accessors
6
+
7
+ attr_accessor :output, :content_hash, :rendered_content
8
+
9
+ def initialize(content_hash, output_stream = nil)
10
+ raise ArgumentError.new('Invalid arguments - expecting a stream and a hash') unless content_hash.is_a?(Hash)
11
+ self.output = output_stream
12
+ self.content_hash = content_hash
13
+ end
14
+
15
+ def render(format)
16
+ self.rendered_content = self.formats[format].to.call(content_hash)
17
+ if output.respond_to?(:puts)
18
+ output.puts rendered_content
19
+ output.close if output.respond_to?(:close) && output != STDOUT
20
+ end
21
+ rendered_content
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module DuperVisor
2
+ VERSION = "1.0.2"
3
+ end
metadata ADDED
@@ -0,0 +1,189 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dupervisor
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Konstantin Gredeskoul
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-07-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: require_dir
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: awesome_print
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: colored2
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: inifile
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: codeclimate-test-reporter
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.12'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.12'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '10.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '10.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.0'
125
+ description: This gem's purpose in life is to freely convert various configurations
126
+ between supported formats, which are currently YAML, JSON and Windows INI file format.
127
+ The gem is named after a popular package supervisord, which uses INI file format
128
+ for it's configuration. This gem will allow you to move supervisord configuration
129
+ into a YAML file, and integrate with other DevOps tools, while generating INI file
130
+ on the fly. When installed, library exposes 'dv' executable, which is a an easy-to-use
131
+ converter between these three formats.
132
+ email:
133
+ - kigster@gmail.com
134
+ executables:
135
+ - dv
136
+ extensions: []
137
+ extra_rdoc_files: []
138
+ files:
139
+ - ".atom-build.json"
140
+ - ".codeclimate.yml"
141
+ - ".gitignore"
142
+ - ".rspec"
143
+ - ".rubocop.yml"
144
+ - ".travis.yml"
145
+ - Gemfile
146
+ - LICENSE
147
+ - README.md
148
+ - Rakefile
149
+ - bin/console
150
+ - bin/setup
151
+ - dupervisor.gemspec
152
+ - exe/dv
153
+ - lib/dupervisor.rb
154
+ - lib/dupervisor/cli.rb
155
+ - lib/dupervisor/config.rb
156
+ - lib/dupervisor/content.rb
157
+ - lib/dupervisor/detector.rb
158
+ - lib/dupervisor/formats.rb
159
+ - lib/dupervisor/formats/ini.rb
160
+ - lib/dupervisor/formats/json.rb
161
+ - lib/dupervisor/formats/yaml.rb
162
+ - lib/dupervisor/main.rb
163
+ - lib/dupervisor/parser.rb
164
+ - lib/dupervisor/renderer.rb
165
+ - lib/dupervisor/version.rb
166
+ homepage: https://github.com/kigster/dupervisor
167
+ licenses: []
168
+ metadata: {}
169
+ post_install_message:
170
+ rdoc_options: []
171
+ require_paths:
172
+ - lib
173
+ required_ruby_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ required_rubygems_version: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ requirements: []
184
+ rubyforge_project:
185
+ rubygems_version: 2.5.1
186
+ signing_key:
187
+ specification_version: 4
188
+ summary: Convert between YAML/JSON format and Windows INI format.
189
+ test_files: []