chaindrive 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ .bin/
18
+ tmp/
19
+ bundle/
20
+ vendor/
21
+ bin/
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ branches:
3
+ only:
4
+ - master
5
+ rvm:
6
+ - 2.0.0
7
+ - 1.9.3
8
+ matrix:
9
+ allow_failures:
10
+ - rvm: ruby-head
11
+ - rvm: rbx
12
+
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # coding: utf-8
2
+ source 'https://rubygems.org'
3
+ ruby '1.9.3'
4
+
5
+ # Specify your gem's dependencies in chaindrive.gemspec
6
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+
3
+ guard :minitest do
4
+ # with Minitest::Unit
5
+ watch(%r{^test/(.*)\/?test_(.*)\.rb})
6
+ watch(%r{^lib/(.*/)?([^/]+)\.rb}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
7
+ watch(%r{^test/test_helper\.rb}) { 'test' }
8
+
9
+ # with Minitest::Spec
10
+ watch(%r{^spec/(.*)_spec\.rb})
11
+ watch(%r{^lib/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
12
+ watch(%r{^spec/spec_helper\.rb}) { 'spec' }
13
+ end
14
+
15
+ guard :minitest do
16
+ # with Minitest::Unit
17
+ watch(%r{^test/(.*)\/?test_(.*)\.rb})
18
+ watch(%r{^lib/(.*/)?([^/]+)\.rb}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
19
+ watch(%r{^test/test_helper\.rb}) { 'test' }
20
+
21
+ # with Minitest::Spec
22
+ watch(%r{^spec/(.*)_spec\.rb})
23
+ watch(%r{^lib/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
24
+ watch(%r{^spec/spec_helper\.rb}) { 'spec' }
25
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 John Bellone
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # Chaindrive [![Build Status](https://secure.travis-ci.org/chaindrive/chaindrive.png?branch=master)](http://travis-ci.org/chaindrive/chaindrive)
2
+ ===================================
3
+
4
+ ## Installation
5
+
6
+ Add this line to your application's Gemfile:
7
+
8
+ gem 'chaindrive'
9
+
10
+ And then execute:
11
+
12
+ $ bundle
13
+
14
+ Or install it yourself as:
15
+
16
+ $ gem install chaindrive
17
+
18
+ ## Usage
19
+
20
+ $ chaindrive help
21
+
22
+ ## Contributing
23
+
24
+ 1. Fork it
25
+ y2. Create your feature branch (`git checkout -b my-new-feature`)
26
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
27
+ 4. Push to the branch (`git push origin my-new-feature`)
28
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'chaindrive/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "chaindrive"
8
+ spec.version = Chaindrive::VERSION
9
+ spec.authors = ["John Bellone"]
10
+ spec.email = ["john.bellone.jr@gmail.com"]
11
+ spec.description = %q{A tool to describe complex software systems.}
12
+ spec.summary = IO.read(File.join(File.dirname(__FILE__), 'README.md'))
13
+ spec.homepage = "http://chaindrive.io"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'thor', '~> 0.18'
22
+ spec.add_dependency 'multi_json', '~> 1.0'
23
+ spec.add_dependency 'hashie', '~> 2.0'
24
+ spec.add_dependency 'colorize', '~> 0.6'
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.3"
27
+ spec.add_development_dependency "rake"
28
+ spec.add_development_dependency 'pry'
29
+ spec.add_development_dependency 'pry-debugger'
30
+ spec.add_development_dependency 'pry-stack_explorer'
31
+ spec.add_development_dependency 'guard-minitest'
32
+ end
data/etc/Gearfile ADDED
@@ -0,0 +1,4 @@
1
+ # coding: utf-8
2
+ source :chaindrive
3
+
4
+ gear 'rbenv'
@@ -0,0 +1,13 @@
1
+ # coding: utf-8
2
+ Chaindrive::Gear.define :rbenv do |gear|
3
+ gear.name = 'rbenv'
4
+ gear.version = '0.1.0'
5
+ gear.description = 'Setup the Ruby environment'
6
+ gear.long_description = 'Setup the Ruby environment'
7
+ gear.homepage = 'https://github.com/chaindrive/gear-rbenv'
8
+ gear.maintainer = {
9
+ name: 'John Bellone',
10
+ email: 'john.bellone.jr@gmail.com',
11
+ github: 'johnbellone'
12
+ }
13
+ end
data/lib/chaindrive.rb ADDED
@@ -0,0 +1,76 @@
1
+ # coding: utf-8
2
+ require 'thor'
3
+ require 'hashie'
4
+ require 'hashie/blash'
5
+ require 'multi_json'
6
+ require 'logger'
7
+ require 'fileutils'
8
+
9
+ require 'chaindrive/version'
10
+ require 'chaindrive/config'
11
+ require 'chaindrive/environment'
12
+ require 'chaindrive/command'
13
+ require 'chaindrive/gear'
14
+ require 'chaindrive/gearbox'
15
+ require 'chaindrive/user'
16
+
17
+ # @abstract
18
+ module Chaindrive
19
+ class << self
20
+
21
+ def chaindrive_path
22
+ File.join(Dir.home, '.chaindrive')
23
+ end
24
+
25
+ def user_config
26
+ File.join(chaindrive_path, 'config.json')
27
+ end
28
+
29
+ def cache
30
+ File.join(user_path, 'cache')
31
+ end
32
+
33
+ def root
34
+ Dir.pwd
35
+ end
36
+
37
+ def root_config
38
+ File.join(root, '.chaindrive/config.json')
39
+ end
40
+
41
+ def logger
42
+ $stdout.sync = true
43
+ @logger ||= Logger.new($stdout).tap do |logger|
44
+ logger.level = Logger::INFO
45
+ logger.formatter = proc do |severity, datetime, progname, msg|
46
+ case severity
47
+ when Logger::ERROR
48
+ return msg.colorize(:magenta)
49
+ when Logger::FATAL
50
+ return msg.colorize(:red)
51
+ when Logger::WARN
52
+ return msg.colorize(:yellow)
53
+ when Logger::INFO
54
+ return msg.colorize(:white)
55
+ end
56
+
57
+ msg
58
+ end
59
+ end
60
+ end
61
+
62
+ # Read and evaluate a file from disk to configure.
63
+ # @param [String] path on disk where file is located
64
+ def load(filename = nil)
65
+ filename ||= File.join(Chaindrive.path, 'Gearfile')
66
+ raise IOError, "'Gearfile' does not exist!" unless File.exists?(filename)
67
+ begin
68
+ Kernel.load(filename)
69
+ rescue LoadError => e
70
+ logger.error "Failed loading '#{filename}': #{e.message}"
71
+ end
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -0,0 +1,43 @@
1
+ # coding: utf-8
2
+ require 'chaindrive/command/config'
3
+ require 'chaindrive/command/install'
4
+ require 'chaindrive/command/upgrade'
5
+
6
+ module Chaindrive
7
+ module Command
8
+
9
+ # Entry point for the command-line interface of the application.
10
+ class CLI < Thor
11
+ option :debug, type: :boolean, desc: 'Debug mode'
12
+ option :config, type: :string, desc: 'Configuration file'
13
+ option :verbose, type: :boolean, desc: 'Verbosity'
14
+
15
+ desc 'init', 'Generates a Gearfile into the working directory.'
16
+ def init
17
+ if File.exists?('Gearfile')
18
+ Chaindrive.logger.error "Gearfile already exists at '#{Dir.pwd}'"
19
+ exit 1
20
+ end
21
+
22
+ Chaindrive.logger.info "Writing Gearfile to '#{Dir.pwd}'"
23
+ FileUtils.cp(File.expand_path('templates/Gearfile', __FILE__), 'Gearfile')
24
+ end
25
+
26
+ desc 'env', 'Prints run-time environment variables.'
27
+ def env
28
+ environment = Environment.new(opts)
29
+ puts environment.inspect
30
+ end
31
+
32
+ desc "config", "View and modify configuration for application"
33
+ subcommand 'config', Config
34
+
35
+ desc 'install', 'Install gear components'
36
+ subcommand 'install', Install
37
+
38
+ desc 'upgrade', 'Upgrade gear components'
39
+ subcommand 'upgrade', Upgrade
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+ module Chaindrive
3
+ module Command
4
+
5
+ # Public - Manages the application configuration and runtime options.
6
+ class Config < Thor
7
+ namespace :config
8
+
9
+ method_option :global, type: :boolean, default: false
10
+ method_option :system, type: :boolean, default: false
11
+ method_option :local, type: :boolean, default: true
12
+ method_option :file, type: :string, default: false
13
+ method_option :unset, type: :boolean, default: false
14
+ desc 'set [-c] [NAME] [VALUE]', 'Set the configuration for the application'
15
+ def set(name, value)
16
+ env = Chaindrive::Environment.new(options)
17
+ if Chaindrive::Config.method_defined?(name.to_sym)
18
+ env.config[name.to_sym] = value
19
+ env.config.save!
20
+ else
21
+ Chaindrive.logger.warn "Configuration can not set '#{name}' property."
22
+ end
23
+ end
24
+
25
+ desc 'get [NAME]', 'Get the configuration for the application'
26
+ def get(name)
27
+ env = Chaindrive::Environment.new(options)
28
+ if Chaindrive::Config.method_defined?(name.to_sym)
29
+ puts env.config[name.to_sym] if env.config.send(name.to_sym)
30
+ else
31
+ Chaindrive.logger.warn "Configuration does not contain '#{name}' property."
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,16 @@
1
+ # coding: utf-8
2
+ module Chaindrive
3
+ module Command
4
+
5
+ class Install < Thor
6
+ default_task :install
7
+
8
+ desc 'install', 'Install the Gear environment.'
9
+ def install
10
+
11
+ end
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ # coding: utf-8
2
+ module Chaindrive
3
+ module Command
4
+
5
+ class Upgrade < Thor
6
+ default_task :upgrade
7
+
8
+ desc 'upgrade', 'Upgrade the environment.'
9
+ def upgrade
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ module Chaindrive
3
+
4
+ # Define the basic settings for the Chaindrive runtime environment. These
5
+ # are directly loaded from on-disk JSON files.
6
+ class Config < Hashie::Dash
7
+ property :user, with: lambda { |h| User.new(h) }
8
+
9
+ # Construct a JSON configuration from disk.
10
+ # @param [String] filename Path to the configuration file.
11
+ def initialize(filename)
12
+ # Load up the disk configuration if it exists.
13
+ super MultiJson.load(IO.read(filename)) if File.exists?(filename)
14
+ end
15
+
16
+ # Write the JSON configuration file to disk.
17
+ # @param [String] filename Path to save configuration file.
18
+ def save(filename)
19
+ # Create the configuration directory if it does not exist.
20
+ dp = File.expand_path(File.dirname(filename))
21
+ FileUtils.mkdir_p(dp) unless File.directory?(dp)
22
+ fail SystemCallError, "Failed saving '#{dp}'" unless File.directory?(dp)
23
+
24
+ IO.write(filename, MultiJson.dump(self, pretty: true))
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+ module Chaindrive
3
+
4
+ # @abstract Define an object container which will house all of the
5
+ # information about a single kick of Chaindrive.
6
+ class Environment
7
+ attr_reader :config, :config_file
8
+
9
+ # Create a new run-time environment object to maintain application state.
10
+ # @param [Hash] opts run-time parameters to affect configuration
11
+ def initialize(opts = {})
12
+ @config_file = opts[:config] if File.exists?(opts[:config])
13
+ @config_file ||= Chaindrive.root_config if File.exists?(Chaindrive.root_config)
14
+ @config_file ||= Chaindrive.user_config
15
+ @config = Config.new(@config_file)
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ require 'chaindrive/gear/provisioner'
3
+ require 'chaindrive/gear/specification'
4
+
5
+ module Chaindrive
6
+
7
+ # @abstract
8
+ module Gear
9
+ class << self
10
+ attr_reader :specifications
11
+
12
+ # Produce a Gear Specification from a block and hash.
13
+ #
14
+ # @param [Hash] name
15
+ # @param [Block] &block
16
+ # @return [Specification]
17
+ def define(name = nil, &block)
18
+ hash = { name: name.to_s } if name
19
+ @@specifications ||= []
20
+ Hashie::Blash.new(hash).tap do |m|
21
+ yield m if block_given?
22
+ return Specification.new(m).tap { |s| @@specifications << s }
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,10 @@
1
+ # coding: utf-8
2
+ module Chaindrive
3
+ module Gear
4
+
5
+ class Provisioner < Hashie::Trash
6
+ property :type, required: true
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ # coding: utf-8
2
+ module Chaindrive
3
+ module Gear
4
+
5
+ # Define representation of a single piece of infrastructure.
6
+ class Specification < Hashie::Trash
7
+ property :name, required: true
8
+ property :version, required: true
9
+ property :description, required: true
10
+ property :license, required: true
11
+ property :maintainer, required: true, transform_with: lambda { |h| h.to_a }
12
+ property :long_description
13
+ property :homepage
14
+ property :provisioner, transform_with: lambda { |h| puts h.inspect }
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ require 'chaindrive/gearbox/specification'
3
+
4
+ module Chaindrive
5
+
6
+ # @abstract The reproducible definition of a system.
7
+ module Gearbox
8
+ class << self
9
+ attr_reader :specifications
10
+
11
+ # Produce a Gearbox Specification from a block and hash.
12
+ #
13
+ # @param [String] role String to define the unique type.
14
+ # @return [Specification] Constructed configuration of Gearbox.
15
+ def define(role = nil, &block)
16
+ hash = { role: role.to_s } if role
17
+ @@specifications ||= []
18
+ Hashie::Blash.new(hash).tap do |m|
19
+ yield m if block_given?
20
+ return Specification.new(m).tap { |s| @@specifications << s }
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ # coding: utf-8
2
+ module Chaindrive
3
+ module Gear
4
+
5
+ class Isolation < Hashie::Mash
6
+
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ # coding: utf-8
2
+ module Chaindrive
3
+ module Gearbox
4
+
5
+ # @abstract
6
+ class Specification < Hashie::Trash
7
+ property :role, required: true
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ module Chaindrive
3
+ class Index
4
+ attr_reader :sources, :gears, :groups
5
+
6
+ def initialize
7
+ @sources = []
8
+ @groups = []
9
+ end
10
+
11
+ def source(source, opts = {})
12
+ source = 'http://chaindrive.io' if source == :chaindrive
13
+ if opts[:prepend]
14
+ @sources = [ source ] | @sources
15
+ else
16
+ @sources << source
17
+ end
18
+
19
+ yield if block_given?
20
+ source
21
+ end
22
+
23
+ def group(*args, &block)
24
+ @groups.concat args
25
+ yield
26
+ ensure
27
+ args.each { @groups.pop }
28
+ end
29
+
30
+ def gear(name, *args)
31
+ spec = { name: name.to_s }
32
+ end
33
+
34
+ def gearbox(name, *args, &block)
35
+
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,4 @@
1
+ # coding: utf-8
2
+ source :chaindrive
3
+
4
+ # gear 'rbenv'
@@ -0,0 +1,14 @@
1
+ # coding: utf-8
2
+ module Chaindrive
3
+
4
+ # @abstract Define the structure which represents a User in the Chaindrive
5
+ # application environment.
6
+ class User < Hashie::Dash
7
+ property :name, required: true
8
+ property :github
9
+ property :email
10
+ property :twitter
11
+ property :homepage
12
+ end
13
+
14
+ end
@@ -0,0 +1,4 @@
1
+ # coding: utf-8
2
+ module Chaindrive
3
+ VERSION = "0.0.1"
4
+ end
@@ -0,0 +1,230 @@
1
+ require 'hashie/hash'
2
+
3
+ module Hashie
4
+ # Blash allows you to create Mash-like objects with a block syntax
5
+ # for creating nested hashes. This is useful for building deeply
6
+ # nested configuration hashes, similar in style to many dsl based
7
+ # configuration files.
8
+ #
9
+ # A Blash will look at the methods you pass it and perform operations
10
+ # based on the following rules:
11
+ #
12
+ # * No punctuation: Returns the value of the hash for that key, or nil if none exists.
13
+ # * With Block (<tt>{...}</tt>): Sets key to and yields a new blash
14
+ # * Assignment (<tt>=</tt>): Sets the attribute of the given method name.
15
+ # * Existence (<tt>?</tt>): Returns true or false depending on whether that key has been set.
16
+ #
17
+ # == Basic Example
18
+ #
19
+ # blash = Blash.new
20
+ # blash.name? # => false
21
+ # blash.name = "Bob"
22
+ # blash.name # => "Bob"
23
+ # blash.name? # => true
24
+ #
25
+ # == Hash Conversion Example
26
+ #
27
+ # hash = {:a => {:b => 23, :d => {:e => "abc"}}, :f => [{:g => 44, :h => 29}, 12]}
28
+ # blash = Blash.new(hash)
29
+ # blash.a.b # => 23
30
+ # blash.a.d.e # => "abc"
31
+ # blash.f.first.g # => 44
32
+ # blash.f.last # => 12
33
+ #
34
+ # == Block Example
35
+ #
36
+ # blash = Blash.new
37
+ # blash.author do |a|
38
+ # a.name = "Michael Bleigh"
39
+ # end
40
+ # blash.author # => <Blash name="Michael Bleigh">
41
+ # blash.author.name # => "Michael Bleigh"
42
+ #
43
+ class Blash < Hash
44
+ include Hashie::PrettyInspect
45
+ alias_method :to_s, :inspect
46
+
47
+ # If you pass in an existing hash, it will
48
+ # convert it to a Blash including recursively
49
+ # descending into arrays and hashes, converting
50
+ # them as well.
51
+ def initialize(source_hash = nil, default = nil, &blk)
52
+ deep_update(source_hash) if source_hash
53
+ default ? super(default) : super(&blk)
54
+ end
55
+
56
+ class << self; alias [] new; end
57
+
58
+ def id #:nodoc:
59
+ self["id"]
60
+ end
61
+
62
+ def type #:nodoc:
63
+ self["type"]
64
+ end
65
+
66
+ alias_method :regular_reader, :[]
67
+ alias_method :regular_writer, :[]=
68
+
69
+ # Retrieves an attribute set in the Blash. Will convert
70
+ # any key passed in to a string before retrieving.
71
+ def custom_reader(key)
72
+ value = regular_reader(convert_key(key))
73
+ yield value if block_given?
74
+ value
75
+ end
76
+
77
+ # Sets an attribute in the Blash. Key will be converted to
78
+ # a string before it is set, and Hashes will be converted
79
+ # into Blashes for nesting purposes.
80
+ def custom_writer(key,value) #:nodoc:
81
+ regular_writer(convert_key(key), convert_value(value))
82
+ end
83
+
84
+ alias_method :[], :custom_reader
85
+ alias_method :[]=, :custom_writer
86
+
87
+ # This is the bang method reader, it will return a new Blash
88
+ # if there isn't a value already assigned to the key requested.
89
+ def initializing_reader(key)
90
+ ck = convert_key(key)
91
+ regular_writer(ck, self.class.new) unless key?(ck)
92
+ regular_reader(ck)
93
+ end
94
+
95
+ def fetch(key, *args)
96
+ super(convert_key(key), *args)
97
+ end
98
+
99
+ def delete(key)
100
+ super(convert_key(key))
101
+ end
102
+
103
+ alias_method :regular_dup, :dup
104
+ # Duplicates the current blash as a new blash.
105
+ def dup
106
+ self.class.new(self, self.default)
107
+ end
108
+
109
+ def key?(key)
110
+ super(convert_key(key))
111
+ end
112
+ alias_method :has_key?, :key?
113
+ alias_method :include?, :key?
114
+ alias_method :member?, :key?
115
+
116
+ # Performs a deep_update on a duplicate of the
117
+ # current blash.
118
+ def deep_merge(other_hash, &blk)
119
+ dup.deep_update(other_hash, &blk)
120
+ end
121
+ alias_method :merge, :deep_merge
122
+
123
+ # Recursively merges this blash with the passed
124
+ # in hash, merging each hash in the hierarchy.
125
+ def deep_update(other_hash, &blk)
126
+ other_hash.each_pair do |k,v|
127
+ key = convert_key(k)
128
+ if regular_reader(key).is_a?(Blash) and v.is_a?(::Hash)
129
+ custom_reader(key).deep_update(v, &blk)
130
+ else
131
+ value = convert_value(v, true)
132
+ value = blk.call(key, self[k], value) if blk
133
+ custom_writer(key, value)
134
+ end
135
+ end
136
+ self
137
+ end
138
+ alias_method :deep_merge!, :deep_update
139
+ alias_method :update, :deep_update
140
+ alias_method :merge!, :update
141
+
142
+ # Performs a shallow_update on a duplicate of the current blash
143
+ def shallow_merge(other_hash)
144
+ dup.shallow_update(other_hash)
145
+ end
146
+
147
+ # Merges (non-recursively) the hash from the argument,
148
+ # changing the receiving hash
149
+ def shallow_update(other_hash)
150
+ other_hash.each_pair do |k,v|
151
+ regular_writer(convert_key(k), convert_value(v, true))
152
+ end
153
+ self
154
+ end
155
+
156
+ def replace(other_hash)
157
+ (keys - other_hash.keys).each { |key| delete(key) }
158
+ other_hash.each { |key, value| self[key] = value }
159
+ self
160
+ end
161
+
162
+ # Will return true if the Blash has had a key
163
+ # set in addition to normal respond_to? functionality.
164
+ def respond_to?(method_name, include_private=false)
165
+ return true if key?(method_name) || method_name.to_s.slice(/[=?]\Z/)
166
+ super
167
+ end
168
+
169
+ def method_missing(method_name, *args, &blk)
170
+ match = method_name.to_s.match(/(.*?)([?=]?)$/)
171
+
172
+ if block_given?
173
+ super unless match[2].empty?
174
+
175
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 0)" if args.size > 1
176
+
177
+ if key?(method_name) && ! self.[](method_name).is_a?(Blash)
178
+ raise TypeError, "key '#{method_name}' already contains a #{self.[](method_name).class}"
179
+ end
180
+
181
+ val = self.[](method_name) || initializing_reader(method_name)
182
+
183
+ yield val
184
+
185
+ return val
186
+ else
187
+ if key?(method_name)
188
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 0)" unless args.empty?
189
+ return self.[](method_name, &blk)
190
+ end
191
+
192
+ case match[2]
193
+ when "="
194
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" unless args.size == 1
195
+ self[match[1]] = args.first
196
+ when "?"
197
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 0)" unless args.empty?
198
+ !!self[match[1]]
199
+ when ""
200
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 0)" unless args.empty?
201
+ default(method_name)
202
+ else
203
+ super
204
+ end
205
+ end
206
+ end
207
+
208
+ protected
209
+
210
+ def convert_key(key) #:nodoc:
211
+ key.to_s
212
+ end
213
+
214
+ def convert_value(val, duping=false) #:nodoc:
215
+ case val
216
+ when self.class
217
+ val.dup
218
+ when Hash
219
+ duping ? val.dup : val
220
+ when ::Hash
221
+ val = val.dup if duping
222
+ self.class.new(val)
223
+ when Array
224
+ val.collect{ |e| convert_value(e) }
225
+ else
226
+ val
227
+ end
228
+ end
229
+ end
230
+ end
data/script/bootstrap ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ bundle install --standalone
@@ -0,0 +1,13 @@
1
+ # coding: utf-8
2
+ require 'chaindrive'
3
+ require 'minitest/spec'
4
+
5
+ describe 'User' do
6
+ before do
7
+ user = Chaindrive::User.new({ name: 'John Bellone' })
8
+ end
9
+
10
+ it 'has a name' do
11
+ user.name.must_equal 'John Bellone'
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,246 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chaindrive
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - John Bellone
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-09-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thor
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.18'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '0.18'
30
+ - !ruby/object:Gem::Dependency
31
+ name: multi_json
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: hashie
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: colorize
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '0.6'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '0.6'
78
+ - !ruby/object:Gem::Dependency
79
+ name: bundler
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '1.3'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '1.3'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rake
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: pry
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: pry-debugger
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: pry-stack_explorer
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: guard-minitest
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ description: A tool to describe complex software systems.
175
+ email:
176
+ - john.bellone.jr@gmail.com
177
+ executables:
178
+ - chaindrive
179
+ extensions: []
180
+ extra_rdoc_files: []
181
+ files:
182
+ - .gitignore
183
+ - .ruby-version
184
+ - .travis.yml
185
+ - Gemfile
186
+ - Guardfile
187
+ - LICENSE.txt
188
+ - README.md
189
+ - Rakefile
190
+ - bin/chaindrive
191
+ - chaindrive.gemspec
192
+ - etc/Gearfile
193
+ - etc/rbenv.gearspec
194
+ - lib/chaindrive.rb
195
+ - lib/chaindrive/command.rb
196
+ - lib/chaindrive/command/config.rb
197
+ - lib/chaindrive/command/install.rb
198
+ - lib/chaindrive/command/upgrade.rb
199
+ - lib/chaindrive/config.rb
200
+ - lib/chaindrive/environment.rb
201
+ - lib/chaindrive/gear.rb
202
+ - lib/chaindrive/gear/provisioner.rb
203
+ - lib/chaindrive/gear/specification.rb
204
+ - lib/chaindrive/gearbox.rb
205
+ - lib/chaindrive/gearbox/isolation.rb
206
+ - lib/chaindrive/gearbox/specification.rb
207
+ - lib/chaindrive/index.rb
208
+ - lib/chaindrive/templates/Gearfile
209
+ - lib/chaindrive/user.rb
210
+ - lib/chaindrive/version.rb
211
+ - lib/hashie/blash.rb
212
+ - script/bootstrap
213
+ - spec/chaindrive/user_spec.rb
214
+ homepage: http://chaindrive.io
215
+ licenses:
216
+ - MIT
217
+ post_install_message:
218
+ rdoc_options: []
219
+ require_paths:
220
+ - lib
221
+ required_ruby_version: !ruby/object:Gem::Requirement
222
+ none: false
223
+ requirements:
224
+ - - ! '>='
225
+ - !ruby/object:Gem::Version
226
+ version: '0'
227
+ required_rubygems_version: !ruby/object:Gem::Requirement
228
+ none: false
229
+ requirements:
230
+ - - ! '>='
231
+ - !ruby/object:Gem::Version
232
+ version: '0'
233
+ requirements: []
234
+ rubyforge_project:
235
+ rubygems_version: 1.8.23
236
+ signing_key:
237
+ specification_version: 3
238
+ summary: ! '# Chaindrive [![Build Status](https://secure.travis-ci.org/chaindrive/chaindrive.png?branch=master)](http://travis-ci.org/chaindrive/chaindrive)
239
+ =================================== ## Installation Add this line to your application''s
240
+ Gemfile: gem ''chaindrive'' And then execute: $ bundle Or install it yourself
241
+ as: $ gem install chaindrive ## Usage $ chaindrive help ## Contributing 1.
242
+ Fork it y2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit
243
+ your changes (`git commit -am ''Add some feature''`) 4. Push to the branch (`git
244
+ push origin my-new-feature`) 5. Create new Pull Request'
245
+ test_files:
246
+ - spec/chaindrive/user_spec.rb