Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Basic implementation of Erlang template helpers

  • Loading branch information...
commit 2a82982e0346116d4882fffb596919c318b112f8 0 parents
@dreverri authored
18 .gitignore
@@ -0,0 +1,18 @@
+*.gem
+*.rbc
+.bundle
+.config
+.yardoc
+Gemfile.lock
+InstalledFiles
+_yardoc
+coverage
+doc/
+lib/bundler/man
+pkg
+rdoc
+spec/reports
+test/tmp
+test/version_tmp
+tmp
+.DS_Store
4 Gemfile
@@ -0,0 +1,4 @@
+source 'https://rubygems.org'
+
+# Specify your gem's dependencies in erlang_template_helper.gemspec
+gemspec
22 LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2012 Daniel Reverri
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
42 README.md
@@ -0,0 +1,42 @@
+# ErlangTemplateHelper
+
+This library allows one to specify an Erlang config file in JSON. This is useful
+when deploying an Erlang application with Chef.
+
+## Usage
+
+* JSON strings are Erlang atoms unless prefixed with `__binary_`, or
+ `__string_`. The prefix `__atom_` is also recognized.
+ * `"ok"` becomes `ok`
+ * `"__binary_0b:"` becomes `<<"0b:">>`
+ * `"__string_127.0.0.1"` becomes `"127.0.0.1"`
+ * `"__atom_riak@127.0.0.1"` becomes `'riak@127.0.0.1'`
+* JSON arrays are Erlang lists unless prefixed with `__tuple`. The prefix
+ `__list` is also recognized.
+ * `[1, 2, 3]` becomes `[1, 2, 3]`
+ * `["__tuple", 1, 2, 3]` becomes `{1, 2, 3}`
+ * `["__list", 1, 2, 3]` becomes `[1, 2, 3]`
+* JSON objects are Erlang proplists.
+ * `{"storage_backend":"bitcask"}` becomes `[{storage_backend, bitcask}]`
+
+```ruby
+> require "erlang_template_helper"
+=> true
+> config = Eth::Config.new({"riak_kv" => {"storage_backend" => "bitcask"}})
+=> [{riak_kv, [{storage_backend, bitcask}]}]
+> puts config.pp
+[
+ {riak_kv, [
+ {storage_backend, bitcask}
+ ]}
+]
+=> nil
+```
+
+## Contributing
+
+1. Fork it
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Commit your changes (`git commit -am 'Added some feature'`)
+4. Push to the branch (`git push origin my-new-feature`)
+5. Create new Pull Request
12 Rakefile
@@ -0,0 +1,12 @@
+#!/usr/bin/env rake
+require "bundler/gem_tasks"
+
+require 'rake/testtask'
+
+Rake::TestTask.new do |t|
+ t.libs << 'lib/erlang_template_helper'
+ t.test_files = FileList['test/lib/erlang_template_helper/*_test.rb']
+ t.verbose = true
+end
+
+task :default => :test
17 erlang_template_helper.gemspec
@@ -0,0 +1,17 @@
+# -*- encoding: utf-8 -*-
+require File.expand_path('../lib/erlang_template_helper/version', __FILE__)
+
+Gem::Specification.new do |gem|
+ gem.authors = ["Daniel Reverri"]
+ gem.email = ["reverri@gmail.com"]
+ gem.description = %q{TODO: Write a gem description}
+ gem.summary = %q{TODO: Write a gem summary}
+ gem.homepage = ""
+
+ gem.files = `git ls-files`.split($\)
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
+ gem.name = "erlang_template_helper"
+ gem.require_paths = ["lib"]
+ gem.version = ErlangTemplateHelper::VERSION
+end
147 lib/erlang_template_helper.rb
@@ -0,0 +1,147 @@
+require "erlang_template_helper/version"
+
+module Eth
+ module InstanceMethods
+ def convert(value)
+ case value
+ when ::String
+ Eth::String.new(value)
+ when ::Array
+ Eth::Array.new(value)
+ when ::Hash
+ Eth::Hash.new(value)
+ when ::TrueClass
+ Eth::Value.new("true")
+ when ::FalseClass
+ Eth::Value.new("false")
+ when ::NilClass
+ Eth::Value.new("null")
+ else
+ Eth::Value.new(value)
+ end
+ end
+
+ def indent(level)
+ "\t" * level
+ end
+ end
+
+ class Value
+ def initialize(val)
+ @val = val
+ end
+
+ def to_s
+ @val
+ end
+
+ def pp(level=0)
+ to_s
+ end
+ end
+
+ class String
+ def initialize(str)
+ @str = str
+ end
+
+ def to_s
+ case @str
+ when /^__atom_(.*)/
+ "'#{$1}'"
+ when /^__binary_(.*)/
+ "<<\"#{$1}\">>"
+ when /^__string_(.*)/
+ "\"#{$1}\""
+ else
+ @str
+ end
+ end
+
+ def pp(level=0)
+ to_s
+ end
+ end
+
+ class Array
+ include InstanceMethods
+
+ def initialize(arr)
+ case arr[0]
+ when "__list"
+ @type = :list
+ @values = arr[1..-1].map { |e| convert(e) }
+ when "__tuple"
+ @type = :tuple
+ @values = arr[1..-1].map { |e| convert(e) }
+ else
+ @type = :list
+ @values = arr.map { |e| convert(e) }
+ end
+ end
+
+ def to_s
+ values1 = @values.map { |v| v.to_s }
+ case @type
+ when :tuple
+ "{#{values1.join(", ")}}"
+ else
+ "[#{values1.join(", ")}]"
+ end
+ end
+
+ def pp(level=0)
+ case @type
+ when :tuple
+ values1 = @values.map { |v| v.pp(level+1) }
+ "{#{values1.join(", ")}}"
+ else
+ values1 = @values.map { |v| "\n#{indent(level+1)}#{v.pp(level+1)}" }
+ "[#{values1.join(", ")}\n#{indent(level)}]"
+ end
+ end
+ end
+
+ class Hash
+ include InstanceMethods
+
+ def initialize(hsh)
+ @hsh = {}
+ hsh.map do |k,v|
+ k1 = Eth::String.new(k.to_s)
+ v1 = convert(v)
+ @hsh[k1] = v1
+ end
+ end
+
+ def to_s
+ values = @hsh.map do |k,v|
+ "{#{k.to_s}, #{v.to_s}}"
+ end
+ "[#{values.join(", ")}]"
+ end
+
+ def pp(level=0)
+ values = @hsh.map do |k,v|
+ "\n#{indent(level+1)}{#{k.to_s}, #{v.pp(level+1)}}"
+ end
+ "[#{values.join(", ")}\n#{indent(level)}]"
+ end
+ end
+
+ class Config
+ include InstanceMethods
+
+ def initialize(value)
+ @config = convert(value)
+ end
+
+ def to_s
+ @config.to_s
+ end
+
+ def pp(level=0)
+ @config.pp(level)
+ end
+ end
+end
3  lib/erlang_template_helper/version.rb
@@ -0,0 +1,3 @@
+module ErlangTemplateHelper
+ VERSION = "0.0.1"
+end
17 test/examples/multi_backend.config
@@ -0,0 +1,17 @@
+[
+ {riak_kv, [
+ {multi_backend_prefix_list, [
+ {<<"0b:">>, be_blocks}
+ ]},
+ {multi_backend, [
+ {be_default, riak_kv_eleveldb_backend, [
+ {cache_size, 47721858},
+ {data_root, "/var/lib/riak/leveldb"},
+ {max_open_files, 50}
+ ]},
+ {be_blocks, riak_kv_bitcask_backend, [
+ {data_root, "/var/lib/riak/bitcask"}
+ ]}
+ ]}
+ ]}
+]
14 test/examples/multi_backend.json
@@ -0,0 +1,14 @@
+{
+ "riak_kv":
+ {
+ "multi_backend_prefix_list": {"__binary_0b:": "be_blocks"},
+ "multi_backend":[
+ ["__tuple", "be_default", "riak_kv_eleveldb_backend", {
+ "cache_size":47721858,
+ "data_root":"__string_/var/lib/riak/leveldb",
+ "max_open_files":50}],
+ ["__tuple", "be_blocks", "riak_kv_bitcask_backend", {
+ "data_root": "__string_/var/lib/riak/bitcask"}]
+ ]
+ }
+}
70 test/lib/erlang_template_helper/erlang_template_helper_test.rb
@@ -0,0 +1,70 @@
+require_relative '../../test_helper'
+require 'json'
+
+describe "string conversions" do
+ it "should default to an atom" do
+ Eth::Config.new("key").to_s.must_equal "key"
+ end
+
+ it "should produce an atom" do
+ Eth::Config.new("__atom_key").to_s.must_equal "'key'"
+ end
+
+ it "should produce a binary" do
+ Eth::Config.new("__binary_key").to_s.must_equal "<<\"key\">>"
+ end
+
+ it "should produce a string" do
+ Eth::Config.new("__string_key").to_s.must_equal "\"key\""
+ end
+end
+
+describe "list conversions" do
+ it "should default to a list" do
+ Eth::Config.new([1, 2, 3]).to_s.must_equal "[1, 2, 3]"
+ end
+
+ it "should produce a list" do
+ Eth::Config.new(["__list", 1, 2, 3]).to_s.must_equal "[1, 2, 3]"
+ end
+
+ it "should produce a tuple" do
+ Eth::Config.new(["__tuple", 1, 2, 3]).to_s.must_equal "{1, 2, 3}"
+ end
+end
+
+describe "object conversions" do
+ it "should produce a proplist" do
+ obj = Eth::Config.new({"storage_backend" => "bitcask"})
+ obj.to_s.must_equal "[{storage_backend, bitcask}]"
+ end
+
+ it "should handle nested objects" do
+ obj = Eth::Config.new({"riak_kv" => {"storage_backend" => "bitcask"}})
+ obj.to_s.must_equal "[{riak_kv, [{storage_backend, bitcask}]}]"
+ end
+
+ it "should handle nested objects with strings" do
+ v = {"http" => {"__string_127.0.0.1" => 8098}}
+ obj = Eth::Config.new(v)
+ obj.to_s.must_equal "[{http, [{\"127.0.0.1\", 8098}]}]"
+ end
+
+ it "should handle nested objects with binaries" do
+ v = {"multi_backend_prefix_list" => {"__binary_0b:" => "be_blocks"}}
+ obj = Eth::Config.new(v)
+ obj.to_s.must_equal "[{multi_backend_prefix_list, [{<<\"0b:\">>, be_blocks}]}]"
+ end
+end
+
+describe "pretty print" do
+ it "should pretty print" do
+ jpath = File.expand_path('../../../examples/multi_backend.json', __FILE__)
+ cpath = File.expand_path('../../../examples/multi_backend.config', __FILE__)
+ j = IO.read(jpath)
+ c = IO.read(cpath)
+ v = JSON.parse(j)
+ obj = Eth::Config.new(v)
+ obj.pp.must_equal c.strip
+ end
+end
2  test/test_helper.rb
@@ -0,0 +1,2 @@
+require 'minitest/autorun'
+require File.expand_path('../../lib/erlang_template_helper.rb', __FILE__)
Please sign in to comment.
Something went wrong with that request. Please try again.