buff-config-2.0.0/0000755000004100000410000000000012755173256013743 5ustar www-datawww-databuff-config-2.0.0/Thorfile0000644000004100000410000000200212755173256015434 0ustar www-datawww-data# encoding: utf-8 $:.push File.expand_path("../lib", __FILE__) require 'bundler' require 'bundler/setup' require 'buff/ruby_engine' require 'buff/config/version' class Default < Thor extend Buff::RubyEngine unless jruby? require 'thor/rake_compat' include Thor::RakeCompat Bundler::GemHelper.install_tasks desc "build", "Build buff-config-#{Buff::Config::VERSION}.gem into the pkg directory" def build Rake::Task["build"].invoke end desc "install", "Build and install buff-config-#{Buff::Config::VERSION}.gem into system gems" def install Rake::Task["install"].invoke end desc "release", "Create tag v#{Buff::Config::VERSION} and build and push buff-config-#{Buff::Config::VERSION}.gem to Rubygems" def release Rake::Task["release"].invoke end end class Spec < Thor namespace :spec default_task :unit desc "unit", "run the project's unit tests" def unit exec "rspec --color --format=documentation spec" end end end buff-config-2.0.0/Gemfile0000644000004100000410000000004712755173256015237 0ustar www-datawww-datasource 'https://rubygems.org' gemspec buff-config-2.0.0/buff-config.gemspec0000644000004100000410000000262212755173256017477 0ustar www-datawww-data# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'buff/config/version' Gem::Specification.new do |spec| spec.name = "buff-config" spec.version = Buff::Config::VERSION spec.authors = ["Jamie Winsor", "Kyle Allan"] spec.email = ["jamie@vialstudios.com", "kallan@riotgames.com"] spec.description = %q{A simple configuration class} spec.summary = spec.description spec.homepage = "https://github.com/RiotGames/buff-config" spec.license = "Apache 2.0" spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^spec/}) spec.require_paths = ["lib"] spec.required_ruby_version = ">= 2.2.0" spec.add_dependency "varia_model", "~> 0.6" spec.add_dependency "buff-extensions", "~> 2.0" spec.add_development_dependency "buff-ruby_engine" spec.add_development_dependency "thor" spec.add_development_dependency "bundler" spec.add_development_dependency "rake" spec.add_development_dependency "rspec" spec.add_development_dependency "fuubar" spec.add_development_dependency "guard" spec.add_development_dependency "guard-rspec" spec.add_development_dependency "guard-spork" spec.add_development_dependency "spork" spec.add_development_dependency "json_spec" end buff-config-2.0.0/.rspec0000644000004100000410000000001412755173256015053 0ustar www-datawww-data--color -fd buff-config-2.0.0/.ruby-version0000644000004100000410000000000612755173256016404 0ustar www-datawww-data2.3.1 buff-config-2.0.0/spec/0000755000004100000410000000000012755173256014675 5ustar www-datawww-databuff-config-2.0.0/spec/buff/0000755000004100000410000000000012755173256015617 5ustar www-datawww-databuff-config-2.0.0/spec/buff/config_spec.rb0000644000004100000410000000142412755173256020424 0ustar www-datawww-datarequire 'spec_helper' describe Buff::Config::Base do subject { Class.new(described_class).new } describe "#to_hash" do it "returns a Hash" do expect(subject.to_hash).to be_a(Hash) end it "contains all of the attributes" do subject.set_attribute(:something, "value") expect(subject.to_hash).to have_key(:something) expect(subject.to_hash[:something]).to eql("value") end end describe "#slice" do before(:each) do subject.set_attribute(:one, nested: "value") subject.set_attribute(:two, nested: "other") @sliced = subject.slice(:one) end it "returns a Hash" do expect(@sliced).to be_a(Hash) end it "contains just the sliced elements" do expect(@sliced.size).to eq(1) end end end buff-config-2.0.0/spec/buff/config/0000755000004100000410000000000012755173256017064 5ustar www-datawww-databuff-config-2.0.0/spec/buff/config/ruby_spec.rb0000644000004100000410000000777612755173256021425 0ustar www-datawww-datarequire 'spec_helper' require 'buff/config/ruby' describe Buff::Config::Ruby do let(:ruby) do %( current_dir = File.dirname(__FILE__) log_level :info log_location STDOUT cookbook_path ['cookbooks'] knife[:foo] = 'bar' knife[:key] = "\#{current_dir}/key.pem" ) end let(:klass) do Class.new(Buff::Config::Ruby) do attribute :log_level attribute :log_location attribute :node_name, default: 'bacon' attribute :cookbook_path attribute :knife, default: {} end end describe 'ClassMethods' do subject { klass } describe '.from_ruby' do it 'returns an instance of the inheriting class' do expect(subject.from_ruby(ruby)).to be_a(subject) end it 'assigns values for each defined attribute' do config = subject.from_ruby(ruby) expect(config[:log_level]).to eq(:info) expect(config[:log_location]).to eq(STDOUT) expect(config[:node_name]).to eq('bacon') expect(config[:cookbook_path]).to eq(['cookbooks']) expect(config[:knife][:foo]).to eq('bar') end it 'properly sets the calling file' do config = subject.from_ruby(ruby, '/home/annie/.chef/knife.rb') expect(config[:knife][:key]).to eq ('/home/annie/.chef/key.pem') end end describe '::from_file' do let(:filepath) { tmp_path.join('test_config.rb').to_s } before { allow(File).to receive(:read).with(filepath).and_return(ruby) } it 'returns an instance of the inheriting class' do expect(subject.from_file(filepath)).to be_a(subject) end it 'sets the object\'s filepath to the path of the loaded filepath' do expect(subject.from_file(filepath).path).to eq(filepath) end context 'given a filepath that does not exist' do before { allow(File).to receive(:read).and_raise(Errno::ENOENT) } it 'raises a Buff::Errors::ConfigNotFound error' do expect { subject.from_file(filepath) }.to raise_error(Buff::Errors::ConfigNotFound) end end end end subject { klass.new } describe '#to_rb' do it 'returns ruby with key values for each attribute' do subject.log_level = :info subject.log_location = STDOUT subject.node_name = 'bacon' subject.cookbook_path = ['cookbooks'] lines = subject.to_ruby.strip.split("\n") expect(lines[0]).to eq('log_level(:info)') expect(lines[1]).to eq('log_location(STDOUT)') expect(lines[2]).to eq('node_name("bacon")') expect(lines[3]).to eq('cookbook_path(["cookbooks"])') end end describe '#from_ruby' do it 'returns an instance of the updated class' do expect(subject.from_ruby(ruby)).to be_a(Buff::Config::Ruby) end it 'assigns values for each defined attribute' do config = subject.from_ruby(ruby) expect(config[:log_level]).to eq(:info) expect(config[:log_location]).to eq(STDOUT) expect(config[:node_name]).to eq('bacon') expect(config[:cookbook_path]).to eq(['cookbooks']) end end describe '#save' do it 'raises a ConfigSaveError if no path is set or given' do subject.path = nil expect { subject.save }.to raise_error(Buff::Errors::ConfigSaveError) end end describe '#reload' do before do subject.path = 'foo/bar.rb' allow(File).to receive(:read).and_return(ruby) end it 'returns self' do expect(subject.reload).to eq(subject) end it 'updates the contents of self from disk' do subject.log_level = :warn subject.node_name = 'eggs' expect(subject.log_level).to eq(:warn) expect(subject.node_name).to eq('eggs') subject.reload expect(subject.log_level).to eq(:info) expect(subject.node_name).to eq('bacon') end it 'raises ConfigNotFound if the path is nil' do subject.path = nil expect { subject.reload }.to raise_error(Buff::Errors::ConfigNotFound) end end end buff-config-2.0.0/spec/buff/config/json_spec.rb0000644000004100000410000000650612755173256021403 0ustar www-datawww-datarequire 'spec_helper' require 'buff/config/json' describe Buff::Config::JSON do let(:json) do %( { "name": "reset", "job": "programmer", "status": "awesome" } ) end describe "ClassMethods" do subject do Class.new(Buff::Config::JSON) do attribute :name, required: true attribute :job end end describe "::from_hash" do let(:hash) { JSON.parse(json) } it "returns an instance of the inheriting class" do expect(subject.from_hash(hash)).to be_a(subject) end end describe "::from_json" do it "returns an instance of the inheriting class" do expect(subject.from_json(json)).to be_a(subject) end it "assigns values for each defined attribute" do config = subject.from_json(json) expect(config[:name]).to eql("reset") expect(config[:job]).to eql("programmer") end end describe "::from_file" do let(:file) { tmp_path.join("test_config.json").to_s } before(:each) do File.open(file, "w") { |f| f.write(json) } end it "returns an instance of the inheriting class" do expect(subject.from_file(file)).to be_a(subject) end it "sets the object's filepath to the path of the loaded file" do expect(subject.from_file(file).path).to eql(file) end context "given a file that does not exist" do it "raises a Buff::Errors::ConfigNotFound error" do expect { subject.from_file(tmp_path.join("asdf.txt")) }.to raise_error(Buff::Errors::ConfigNotFound) end end end end subject do Class.new(Buff::Config::JSON) do attribute :name, required: true attribute :job end.new end describe "#to_json" do before(:each) do subject.name = "reset" subject.job = "programmer" end it "returns JSON with key values for each attribute" do hash = parse_json(subject.to_json) expect(hash).to have_key("name") expect(hash["name"]).to eql("reset") expect(hash).to have_key("job") expect(hash["job"]).to eql("programmer") end end describe "#from_json" do it "returns an instance of the updated class" do expect(subject.from_json(json)).to be_a(Buff::Config::JSON) end it "assigns values for each defined attribute" do config = subject.from_json(json) expect(config.name).to eql("reset") expect(config.job).to eql("programmer") end end describe "#save" do it "raises a ConfigSaveError if no path is set or given" do subject.path = nil expect { subject.save }.to raise_error(Buff::Errors::ConfigSaveError) end end describe "#reload" do before(:each) do subject.path = tmp_path.join('tmpconfig.json').to_s subject.save end it "returns self" do expect(subject.reload).to eql(subject) end it "updates the contents of self from disk" do original = subject.class.from_file(subject.path) subject.job = "programmer" subject.save expect(original.job).to be_nil original.reload expect(original.job).to eql("programmer") end it "raises ConfigNotFound if the path is nil" do subject.path = nil expect { subject.reload }.to raise_error(Buff::Errors::ConfigNotFound) end end end buff-config-2.0.0/spec/spec_helper.rb0000644000004100000410000000150012755173256017507 0ustar www-datawww-data$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) require 'rspec' require 'buff/ruby_engine' require 'json_spec' def setup_rspec RSpec.configure do |config| config.include JsonSpec::Helpers config.expect_with :rspec do |c| c.syntax = :expect end config.mock_with :rspec config.filter_run focus: true config.run_all_when_everything_filtered = true config.before(:each) { clean_tmp_path } end end def app_root @app_root ||= Pathname.new(File.expand_path('../../', __FILE__)) end def clean_tmp_path FileUtils.rm_rf(tmp_path) FileUtils.mkdir_p(tmp_path) end def tmp_path app_root.join('spec', 'tmp') end if Buff::RubyEngine.jruby? require 'buff/config' setup_rspec else require 'spork' Spork.prefork { setup_rspec } Spork.each_run { require 'buff/config' } end buff-config-2.0.0/.travis.yml0000644000004100000410000000036212755173256016055 0ustar www-datawww-datalanguage: ruby cache: - bundler dist: trusty branches: only: - master before_install: - gem update --system - gem install bundler script: "bundle exec thor spec" matrix: include: - rvm: 2.2.5 - rvm: 2.3.1 - rvm: ruby-head buff-config-2.0.0/lib/0000755000004100000410000000000012755173256014511 5ustar www-datawww-databuff-config-2.0.0/lib/buff/0000755000004100000410000000000012755173256015433 5ustar www-datawww-databuff-config-2.0.0/lib/buff/config.rb0000644000004100000410000000116312755173256017226 0ustar www-datawww-datarequire 'buff/extensions' require 'varia_model' require 'forwardable' module Buff require_relative 'config/errors' module Config class Base extend Forwardable include VariaModel attr_accessor :path def_delegator :to_hash, :slice def_delegator :to_hash, :slice! def_delegator :to_hash, :extract! # @param [String] path # @param [Hash] attributes def initialize(path = nil, attributes = {}) @path = File.expand_path(path) if path mass_assign(attributes) end def to_hash super.deep_symbolize_keys end end end end buff-config-2.0.0/lib/buff/config/0000755000004100000410000000000012755173256016700 5ustar www-datawww-databuff-config-2.0.0/lib/buff/config/ruby.rb0000644000004100000410000001113612755173256020210 0ustar www-datawww-datarequire 'buff/config' module Buff module Config class Ruby < Config::Base class Evaluator class << self # Parse the contents of the Ruby file into a Hash. # # @param [String] contents # @param [String] path file that should be used as __FILE__ # during eval # @param [Object] context the parent Config object # # @return [Hash] def parse(contents, path=nil, context=nil) self.new(contents, path, context).send(:attributes) end end # @param [String] contents # @param [String] path # @param [Object] context def initialize(contents, path=nil, context=nil) path ||= "(buff-config)" @context = context @attributes = Hash.new instance_eval(contents, path) rescue Exception => ex raise Errors::InvalidConfig, ex end # @see {Buff::Config::Ruby.platform_specific_path} def platform_specific_path(path) Buff::Config::Ruby.platform_specific_path(path) end private attr_reader :attributes def method_missing(m, *args, &block) if args.size > 0 attributes[m.to_sym] = (args.length == 1) ? args[0] : args elsif @context && @context.respond_to?(m) @context.send(m, *args, &block) else Proxy.new end end class Proxy def method_missing(m, *args, &block) self end end end class << self # @param [String] contents # @param [String] path # # @return [Buff::Config::Ruby] def from_ruby(contents, path=nil) new.from_ruby(contents, path) end # @param [String] path # # @raise [Buff::Errors::ConfigNotFound] # # @return [Buff::Config::Ruby] def from_file(path) path = File.expand_path(path) contents = File.read(path) new(path).from_ruby(contents, path) rescue TypeError, Errno::ENOENT, Errno::EISDIR raise Errors::ConfigNotFound, "No configuration found at: '#{path}'" end # Converts a path to a path usable for your current platform # # @param [String] path # # @return [String] def platform_specific_path(path) if RUBY_PLATFORM =~ /mswin|mingw|windows/ system_drive = ENV['SYSTEMDRIVE'] ? ENV['SYSTEMDRIVE'] : "" path = win_slashify File.join(system_drive, path.split('/')[2..-1]) end path end private # Convert a unixy filepath to a windowsy filepath. Swaps forward slashes for # double backslashes # # @param [String] path # filepath to convert # # @return [String] # converted filepath def win_slashify(path) path.gsub(File::SEPARATOR, (File::ALT_SEPARATOR || '\\')) end end def initialize(path = nil, options = {}) super from_ruby(File.read(path), path) if path && File.exists?(path) end # @raise [Buff::Errors::InvalidConfig] # # @return [Buff::Config::Ruby] def from_ruby(contents, path=nil) hash = Buff::Config::Ruby::Evaluator.parse(contents, path, self) mass_assign(hash) self end # Convert the result to Ruby. # # @return [String] def to_ruby self.to_hash.map do |k,v| value = if const = find_constant(v) const else v.inspect end "#{k.to_s}(#{value})" end.join("\n") end alias_method :to_rb, :to_ruby def save(destination = self.path) if destination.nil? raise Errors::ConfigSaveError, "Cannot save configuration without a destination. " + "Provide one to save or set one on the object." end FileUtils.mkdir_p(File.dirname(destination)) File.open(destination, 'w+') do |f| f.write(to_ruby) end end # Reload the current configuration file from disk # # @return [Buff::Config::Ruby] def reload mass_assign(self.class.from_file(path).to_hash) self end private def find_constant(name) Module.constants.find do |const| begin Module.const_get(const) == name rescue NameError; end end end end end end buff-config-2.0.0/lib/buff/config/errors.rb0000644000004100000410000000026012755173256020537 0ustar www-datawww-datamodule Buff module Errors class ConfigNotFound < StandardError; end class InvalidConfig < StandardError; end class ConfigSaveError < StandardError; end end end buff-config-2.0.0/lib/buff/config/json.rb0000644000004100000410000000327312755173256020203 0ustar www-datawww-datarequire 'json' require 'buff/config' module Buff module Config class JSON < Config::Base class << self # @param [String] data # # @return [Buff::Config::JSON] def from_json(data) new.from_json(data) end # @param [Hash] hash # # @return [Buff::Config::JSON] def from_hash(hash) new.from_hash(hash) end # @param [String] path # # @raise [Buff::Errors::ConfigNotFound] # # @return [Buff::Config::JSON] def from_file(path) path = File.expand_path(path) data = File.read(path) new(path).from_json(data) rescue TypeError, Errno::ENOENT, Errno::EISDIR raise Errors::ConfigNotFound, "No configuration found at: '#{path}'" end end # @see {VariaModel#from_json} # # @raise [Buff::Errors::InvalidConfig] # # @return [Buff::Config::JSON] def from_json(*args) super rescue ::JSON::ParserError => ex raise Errors::InvalidConfig, ex end def save(destination = self.path) if destination.nil? raise Errors::ConfigSaveError, "Cannot save configuration without a destination. Provide one to save or set one on the object." end FileUtils.mkdir_p(File.dirname(destination)) File.open(destination, 'w+') do |f| f.write(::JSON.pretty_generate(self.to_hash)) end end # Reload the current configuration file from disk # # @return [Buff::Config::JSON] def reload mass_assign(self.class.from_file(path).to_hash) self end end end end buff-config-2.0.0/lib/buff/config/version.rb0000644000004100000410000000007412755173256020713 0ustar www-datawww-datamodule Buff module Config VERSION = "2.0.0" end end buff-config-2.0.0/lib/buff-config.rb0000644000004100000410000000003712755173256017223 0ustar www-datawww-datarequire_relative 'buff/config' buff-config-2.0.0/.gitignore0000644000004100000410000000023212755173256015730 0ustar www-datawww-data*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp buff-config-2.0.0/CONTRIBUTING.md0000644000004100000410000000066512755173256016203 0ustar www-datawww-data# Contributing ## Running tests ### Install prerequisites Install the latest version of [Bundler](http://gembundler.com) $ gem install bundler Clone the project $ git clone git://github.com/RiotGames/buff-config.git and run: $ cd buff-config $ bundle install Bundler will install all gems and their dependencies required for testing and developing. ### Running unit (RSpec) tests $ bundle exec guard start buff-config-2.0.0/LICENSE0000644000004100000410000000113012755173256014743 0ustar www-datawww-dataCopyright 2012-2013 Riot Games Jamie Winsor () Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. buff-config-2.0.0/README.md0000644000004100000410000000155712755173256015232 0ustar www-datawww-data# Buff::Config [![Gem Version](https://badge.fury.io/rb/buff-config.png)](http://badge.fury.io/rb/buff-config) [![Build Status](https://travis-ci.org/RiotGames/buff-config.png?branch=master)](https://travis-ci.org/RiotGames/buff-config) A simple configuration class ## Installation Add this line to your application's Gemfile: gem 'buff-config' And then execute: $ bundle Or install it yourself as: $ gem install buff-config ## Usage require 'buff/config/json' class MyConfig < Buff::Config::JSON attribute 'chef.chef_server_url' end my_config = MyConfig.new my_config.chef.chef_server_url #=> nil # Authors and Contributors * Jamie Winsor () * Kyle Allan () Thank you to all of our [Contributors](https://github.com/RiotGames/buff-config/graphs/contributors), testers, and users. buff-config-2.0.0/Guardfile0000644000004100000410000000070412755173256015571 0ustar www-datawww-datanotification :off guard "spork" do watch('Gemfile') watch('spec/spec_helper.rb') { :rspec } watch(%r{^spec/support/.+\.rb$}) { :rspec } end guard "rspec", cli: "--color --drb --format Fuubar", all_on_start: false, all_after_pass: false do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch('spec/spec_helper.rb') { "spec" } watch(%r{^spec/support/.+\.rb$}) { "spec" } end