dupervisor 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []