Skip to content

Commit

Permalink
Merge branch 'dev' of github.com:cloud66/alterant
Browse files Browse the repository at this point in the history
# Conflicts:
#	README.md
  • Loading branch information
khash committed Oct 24, 2018
2 parents 64d3d2c + 0f903de commit d5fa2cc
Show file tree
Hide file tree
Showing 33 changed files with 705 additions and 0 deletions.
1 change: 1 addition & 0 deletions .ruby-gemset
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
alterant
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ruby-2.5.1
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"cSpell.words": [
"fqin"
]
}
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
source 'https://rubygems.org'
gemspec
50 changes: 50 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
PATH
remote: .
specs:
alterant (0.0.1)
colorize (~> 0.8)
diffy (~> 3.2)
json (~> 1.4)
jsonpath (~> 0.9)
mini_racer (~> 0.2)
thor (~> 0.20)

GEM
remote: https://rubygems.org/
specs:
colorize (0.8.1)
diffy (3.2.1)
ffi (1.9.25)
json (1.8.6)
jsonpath (0.9.4)
multi_json
to_regexp (~> 0.2.1)
libv8 (6.7.288.46.1)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
mini_racer (0.2.3)
libv8 (>= 6.3)
multi_json (1.13.1)
rake (10.5.0)
rb-fsevent (0.10.3)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
rerun (0.13.0)
listen (~> 3.0)
ruby_dep (1.5.0)
thor (0.20.0)
to_regexp (0.2.1)

PLATFORMS
ruby

DEPENDENCIES
alterant!
bundler (~> 1.14)
rake (~> 10.0)
rerun (~> 0.13)

BUNDLED WITH
1.16.6
2 changes: 2 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require "bundler/gem_tasks"
task :default => :spec
34 changes: 34 additions & 0 deletions alterant.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)

require 'alterant/version'

Gem::Specification.new do |spec|
spec.name = "alterant"
spec.version = Alterant::VERSION
spec.authors = ["Khash Sajadi"]
spec.email = ["khash@cloud66.com"]

spec.summary = %q{Alterant gem and command line}
spec.description = %q{Alterant is a tool to alter configuration files}
spec.homepage = "https://github.com/cloud66/alterant"
spec.license = 'Nonstandard'

spec.files = Dir.glob("{bin,lib}/**/*") + %w(README.md)
spec.bindir = "bin"
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]

spec.add_development_dependency "bundler", "~> 1.14"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "rerun", "~> 0.13"

spec.add_dependency 'jsonpath', '~>0.9'
spec.add_dependency 'json', '~> 1.4'
spec.add_dependency 'thor', '~> 0.20'
spec.add_dependency 'mini_racer', '~> 0.2'
spec.add_dependency 'colorize', '~> 0.8'
spec.add_dependency 'diffy', '~> 3.2'
end
170 changes: 170 additions & 0 deletions bin/alterant
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#!/usr/bin/env ruby
require 'thor'
require 'yaml'
require 'colorize'
require_relative '../lib/alterant'

module Alterant
class AlterantCLI < Thor
package_name "alterant"

desc "version", "Show Alterant version"
def version
say "#{::Alterant::APP_NAME} #{::Alterant::VERSION}\n#{::Alterant::COPYRIGHT_MESSAGE}"
end

desc "update", "Update Alterant to the latest version"
option :version, type: :string, desc: "Force a specific version"
def update
say "Updating Alterant..."
unless options[:version]
say `gem install alterant --no-ri --no-rdoc`
else
say `gem install alterant -v #{options[:version]} --no-ri --no-rdoc`
end
end

desc "modify", "Runs the given script against a file"
option :modifier, type: :string, desc: "Alterant JS script"
option :in, type: :string, desc: "Input configuration file"
option :out, type: :string, desc: "Output configuration file"
option :output_format, type: :string, enum: ['yaml', 'json'], default: 'yaml', desc: "Output format"
option :diff, type: :boolean, default: false, desc: "Return the diff instead of the output itself"
option :debug, type: :boolean, default: false
option :input_format, type: :string, enum: ['yaml', 'json'], default: 'yaml', desc: "Input format if it's based on a stream and not a file"
option :overwrite, type: :boolean, default: false
def modify
$debug = options[:debug] || false
overwrite = options[:overwrite]
diff = options[:diff]
output_format = options[:output_format]
input_format = options[:input_format]

in_file = options[:in]
if !in_file
STDERR.puts "No input file provided. Use --in option".red
exit(1)
end
if in_file != '-'
unless File.exists?(in_file)
STDERR.puts "Input file #{in_file} not found".red
exit(1)
end
# detect the type
input_ext = File.extname(in_file)
unless ['.yaml', '.yml', '.json'].include? input_ext
STDERR.puts "Only yaml and json files are supported for input".red
exit(1)
end
if ['.yaml', '.yml'].include?(input_ext)
input_format = 'yaml'
else
input_format = 'json'
end

output_format = input_format if !output_format
end

modifier_file = options[:modifier]
if !modifier_file
STDERR.puts "No script file provided. Use --modifier option".red
exit(1)
end
unless File.exists?(modifier_file)
STDERR.puts "Modifier file #{modifier_file} not found".red
exit(1)
end

out_file = options[:out]
if out_file
if !overwrite && File.exists?(out_file)
STDERR.puts "Output file #{out_file} already exists. Use --overwrite flag to overwrite it".red
exit(1)
end
else
out_file = '-' # output to stdout
end


data = []
if in_file == '-'
input_text = STDIN.read
else
input_text = File.read(in_file)
end

if input_format == 'yaml'
if input_text.empty?
STDERR.puts "Empty input file".red
exit(2)
end

input_text.split('---').each_with_index do |part, idx|
part_data = YAML.load(part)
data << part_data
end

else
file_data = JSON.parse(input_text)
if file_data.is_a? Array
data = file_data
else
data = [file_data]
end
end

run_context = {}
modifier = File.read(modifier_file)
if in_file != '-'
run_context[:basedir] = File.dirname(in_file)
end

run_context[:js_preload] = ::Alterant::Classes.LoadClasses
alter = ::Alterant::Alterant.new(input: data, modifier: modifier, filename: modifier_file, options: run_context)
results = alter.execute(timeout: 500)

if results.nil?
STDERR.puts "Aborting".red
exit(2)
end

if output_format == 'yaml' || output_format == 'yml'
converted_as_text = results.map { |r| r.to_yaml }.join("\n")
input = data.map { |r| r.to_yaml }.join("\n")
elsif output_format == 'json'
converted_as_text = JSON.pretty_generate(results)
input = JSON.pretty_generate(data)
end

if diff
output_as_text = Diffy::Diff.new(input, converted_as_text)
else
output_as_text = converted_as_text
end

if out_file == "-"
puts output_as_text
else
File.open(out_file, 'w') do |file|
file.write(output_as_text)
end
end
return true
rescue ::Alterant::ParseError => exc
STDERR.puts "Syntax error: #{exc.message}".red
return false
rescue ::Alterant::RuntimeError => exc
STDERR.puts "Runtime error: #{exc.message}".red
return false
rescue => exc
if $debug
raise
else
STDERR.puts exc
end
return false
end
end

AlterantCLI.start
end
14 changes: 14 additions & 0 deletions bin/console
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env ruby

require "bundler/setup"
require "alterant"

# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.

# (If you use this, don't forget to add pry to your Gemfile!)
# require "pry"
# Pry.start

require "irb"
IRB.start(__FILE__)
8 changes: 8 additions & 0 deletions bin/setup
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx

bundle install

# Do any other automated setup that you need to do here
6 changes: 6 additions & 0 deletions lib/alterant.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
require_relative 'alterant/version'
require_relative 'alterant/utils'
require_relative 'alterant/loader'

module Alterant
end
59 changes: 59 additions & 0 deletions lib/alterant/alterant.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
module Alterant
class Alterant
attr_reader :basedir

# input is a hash
# filename is the modifier filename use for the backtrace
# modifier is the script in string
def initialize(input:, modifier:, filename:, options: {})
@modifier = modifier
@filename = filename
@input = input
@basedir = options[:basedir]
@js_preload = options[:js_preload] || []
end

# timeout is in ms
# returns a hash
def execute(timeout: 500, max_memory: 5000000)
jpath = ::Alterant::Helpers::Jpath.new

result = []
snapshot = MiniRacer::Snapshot.new("$$ = #{@input.to_json};\n" + @js_preload.join("\n")) # this is more efficient but we lose debug info (filename) of helper classes

isolate = MiniRacer::Isolate.new(snapshot)
@input.each_with_index do |input, idx|
ctx = ::MiniRacer::Context.new(isolate: isolate, timeout: timeout, max_memory: max_memory)
ctx.eval("$ = #{input.to_json}")
ctx.eval("$['fetch'] = function(key) { return jpath.fetch(JSON.stringify($), key); }")
ctx.attach('jpath.fetch', proc{|x, y| jpath.fetch(x, y)})
ctx.attach('console.log', proc{|x| STDERR.puts("DEBUG: #{x.inspect}") if $debug })
ctx.attach('console.exception', proc{|x| raise ::Alterant::RuntimeError, x })
ctx.attach('$$.push', proc{|x| result << x })
ctx.attach('$.index', proc{ idx })
ctx.attach('YamlReader', ::Alterant::Classes::YamlReader.new(self, ctx))
ctx.attach('JsonReader', ::Alterant::Classes::JsonReader.new(self, ctx))

ctx.eval(@modifier, filename: @filename)
pre_convert = ctx.eval("JSON.stringify($)")
converted = JSON.parse(pre_convert)
result << converted

ctx.dispose
isolate.idle_notification(100)
rescue ::MiniRacer::RuntimeError => exc
if $debug
raise
else
raise ::Alterant::ParseError, "part: #{idx} - #{exc.message}, #{exc.backtrace.first}"
end
rescue ::Alterant::AlterantError => exc
STDERR.puts exc.message.red
return nil
end

return result
end

end
end
20 changes: 20 additions & 0 deletions lib/alterant/classes/classes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Alterant
module Classes
CLASSES = {}

def self.LoadClasses
# ::Alterant::Classes.constants.map(&::Alterant::Classes.method(:const_get)).grep(Class) do |c|
# name = c.name.split('::').last
# ::Alterant::Classes::CLASSES[name] = c
# end

# load all JS files in the classes dir and construct a long text
js_preload = []
Dir["#{__dir__}/*.js"].each do |f|
js_preload << File.read(f)
end

return js_preload
end
end
end
Loading

0 comments on commit d5fa2cc

Please sign in to comment.