clustr 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZWRlNjk2ZGY1YTM1OTA2YjI4MWJiMWM3YjI1Y2VlMzlhZmIxNjEzMw==
5
+ data.tar.gz: !binary |-
6
+ OWRhYzAwODI2ZGQxZjU1MjljZGEwYzEwMzlkNDI2YjUzNmY4YTE1Yg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NzRiZDkzYjhhNzMwNWYxYzNlYjdiYWRiNTZlMGM5ZTFiMzhhYTU1M2FkNTdh
10
+ ZDQyMzc1NDA3ODFkOGMyZTc1YmM1MGIxZTVhZTZmYTI4YmM4YjY1OGQzN2Zj
11
+ Yzg4MTNlMTE4M2ZhZDYxNzMwNDVhN2ZmNmMxMDc1ZGMzZmZkZDE=
12
+ data.tar.gz: !binary |-
13
+ MjJmOGEzYWY5ZDQ3OTAwYjIyZDBkYTRiMDY4NTRhNWQwNWI1NDc4MDE2MzE4
14
+ OGY3NDllNTRiMjU3NTBhZjc0MjAxMjUxMDdlZDM5NTE5Yzc0ZTcwNGJmMTE0
15
+ ZWQwYjhlZWY4NDk1NDMwMDY1YzI5ZjFjM2YzZDc4ZTYyZGVhZTA=
data/.gitignore ADDED
@@ -0,0 +1,17 @@
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
+ tmp
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ clustr
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0-p353
data/.travis.yml ADDED
@@ -0,0 +1,29 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.1.0
5
+ - 2.0.0-p353
6
+ - ruby-head
7
+ notifications:
8
+ flowdock:
9
+ secure: TI1C6kvdJrMD7m4ImSvf9cU8+1UBrpzlcsfwu2Q+1MYyEz2eBP819eBVF6g/HQM1Z8KU/7ShCtDA2NJQZXn9HfSbT4pebaAcr7FnyEDuWB/38F6/kZdzG71G10SkaRi4kIp4CbaYovaYrO7simeVoYdtK5RWgxGWcnGML8ubL8Y=
10
+ before_deploy:
11
+ - yard doc
12
+ deploy:
13
+ - provider: s3
14
+ access_key_id: AKIAIQXC7KNC6CPV3RMA
15
+ bucket: clustr.koala.im
16
+ skip_cleanup: true
17
+ local-dir: doc
18
+ edge: true
19
+ region: eu-west-1
20
+ endpoint: s3-eu-west-1.amazonaws.com
21
+ on:
22
+ branch: master
23
+ secret_access_key:
24
+ secure: dNZcJd61mj56+nCkOHpmKt+hcwB3KLpvViRLYUKR4G5isgCfkPtoe8PeYx9y/BiNuP0dXGu/Ij7Wgl/BuPGdp6VeNecPZAjWhgsTSuHp7WFlax4WPBe3qdtYWkrws1Gy4vN5da4zN3sY8qWxnu+51t1Im5nibz/fxHnY9THV9q8=
25
+ - provider: rubygems
26
+ on:
27
+ branch: master
28
+ api_key:
29
+ secure: Kkwrzg+2dJrrShwVceco8EEP8Kc9BsKvDWIK6pFrdalZRY4sDG7BC7phPOmQPW40Qvl9f3qaZFjpohN0/QLDlF48Zg2AUBGuKfWpQ7V+dTL7hd6ZewFEs/5vyoofEf7BkSE9NFrsuOymEtHH+MJjqY+aIFg4YrNRcoEvCFuEZRk=
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in clustr.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 davidkelley
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,31 @@
1
+ # Clustr
2
+
3
+ [![Coverage Status](https://coveralls.io/repos/davidkelley/clustr/badge.png)](https://coveralls.io/r/davidkelley/clustr) [![Build Status](https://travis-ci.org/davidkelley/clustr.svg?branch=master)](https://travis-ci.org/davidkelley/clustr) [![Code Climate](https://codeclimate.com/github/davidkelley/clustr.png)](https://codeclimate.com/github/davidkelley/clustr) [![Dependency Status](https://gemnasium.com/davidkelley/clustr.svg)](https://gemnasium.com/davidkelley/clustr)
4
+
5
+ TODO: Write a gem description
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'clustr'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install clustr
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( http://github.com/<my-github-username>/clustr/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ task :default => :spec
5
+ RSpec::Core::RakeTask.new
data/bin/clustr ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'clustr/cli'
3
+ Clustr::CLI.start
data/clustr.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'clustr/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "clustr"
8
+ spec.version = Clustr::VERSION
9
+ spec.authors = ["davidkelley"]
10
+ spec.email = ["david.james.kelley@gmail.com"]
11
+ spec.summary = %q{Ruby gem that enables EC2 instances to cluster together.}
12
+ spec.description = %q{This gem uses Amazon SimpleDB and other adapters, to track namespaced attributes for instances that need to cluster together. It provides a simple CLI which can be used in conjunction with other shell commands. See examples/ for example Cloudformation Templates.}
13
+ spec.homepage = "https://github.com/davidkelley/clustr"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "thor"
22
+ spec.add_dependency "aws-sdk"
23
+ spec.add_dependency "terminal-table"
24
+ spec.add_dependency "nokogiri", ">= 1.4.4"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.5"
27
+ spec.add_development_dependency "rspec", "~> 2.6"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "yard"
30
+ spec.add_development_dependency "faker"
31
+ spec.add_development_dependency "redcarpet"
32
+ spec.add_development_dependency "cucumber"
33
+ spec.add_development_dependency "aruba"
34
+ spec.add_development_dependency "fakefs"
35
+ spec.add_development_dependency "factory_girl"
36
+ spec.add_development_dependency "codeclimate-test-reporter"
37
+ spec.add_development_dependency 'coveralls'
38
+ end
@@ -0,0 +1,18 @@
1
+ module Clustr
2
+ module Adapter
3
+ # Base class for all implemented Adapters.
4
+ class Base
5
+
6
+ attr_reader :key
7
+
8
+ # Initializes a new Adapter class and associates
9
+ # the unique key for this cluster to the @key
10
+ # variable. This key is required for all Adapters.
11
+ def initialize(options)
12
+ @key = options["key"]
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,53 @@
1
+ module Clustr
2
+ module Adapter
3
+ # Implements the SimpleDB adapter, which controls a cluster
4
+ # and its members through the usage of SimpleDB as a centralized,
5
+ # high-availability data store.
6
+ #
7
+ # SimpleDB is capable of storing unique node identifiers along
8
+ # with additional information that is to be associated with them.
9
+ class SimpleDB < Clustr::Adapter::Base
10
+
11
+ # Initializes a new SimpleDB Adapter class. If the domain
12
+ # that has been defined in the @key attribute does not exist,
13
+ # it will create it.
14
+ def initialize
15
+ super
16
+ sdb.domains.create(@key) unless domain.exists?
17
+ end
18
+
19
+ # Add a new node to the SimpleDB domain.
20
+ #
21
+ # @param [string] id Unique ID of the node to add.
22
+ # @param [integer] created timestamp of the node.
23
+ # @param [hash] properties additional properties to add for the node.
24
+ #
25
+ # @return [boolean] True if node was added successfully, false otherwise.
26
+ def add(id, created, properties = {})
27
+ item = domain.items[id]
28
+ properties.each { |k, v| item.attributes[k] = v }
29
+ item.attributes["joined"] = created
30
+ true
31
+ end
32
+
33
+ # Return an array of the unique identifiers for each node
34
+ # in the cluster.
35
+ #
36
+ # @return [array] an array containing the unique ids for each node.
37
+ def members
38
+ domain.items.collect(&:name)
39
+ end
40
+
41
+ private
42
+
43
+ def domain
44
+ sdb.domains[@key]
45
+ end
46
+
47
+ def sdb
48
+ @sdb ||= AWS::SimpleDB.new
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,53 @@
1
+ require "clustr/adapter/base"
2
+
3
+ require "clustr/adapter/simpledb"
4
+
5
+ module Clustr
6
+ # Contains different adapters for controlling the
7
+ # underlying logic for determining current members
8
+ # and adding new members to a cluster through the
9
+ # usage of different technologies,
10
+ # such as {Clustr::Adapter::SimpleDB}.
11
+ module Adapter
12
+
13
+ # Instantiates and returns a new Adapter class
14
+ # which has been determined from the options hash
15
+ # that has been passed in.
16
+ #
17
+ # @example Initializing a new adapter
18
+ # sdb = Clustr::Adapter.from_options({
19
+ # "adapter" => "SimpleDB",
20
+ # "key" => "foobar"
21
+ # })
22
+ #
23
+ # @note The named adapter must equal the defined class name.
24
+ #
25
+ # @param [hash] options to use, must contain an "adapter" key.
26
+ #
27
+ # @return [object] An instantiated adapter object.
28
+ def self.from_options(options)
29
+ name = options["adapter"]
30
+ self[name].new(options)
31
+ end
32
+
33
+ # Return the constantized adapter class.
34
+ #
35
+ # @param [string] adapter to return.
36
+ #
37
+ # @return [class] The uninitialized Adapter, nil if undefined.
38
+ def self.[](adapter)
39
+ self.const_get adapter
40
+ end
41
+
42
+ # Determines if the named adapter that has been passed in
43
+ # is a defined and implemented Adapter.
44
+ #
45
+ # @param [string] adapter name
46
+ #
47
+ # @return [boolean] True if the Adapter is implemented, false otherwise.
48
+ def self.exists?(adapter)
49
+ self.const_defined? adapter
50
+ end
51
+
52
+ end
53
+ end
data/lib/clustr/cli.rb ADDED
@@ -0,0 +1,94 @@
1
+ require 'thor'
2
+ require 'clustr'
3
+ require 'terminal-table'
4
+
5
+ require 'clustr/log'
6
+
7
+ module Clustr
8
+ # Provides a Command Line Interface for the Launcher gem. See the method definitions
9
+ # for more information on its usage.
10
+ #
11
+ # @example Checking the version
12
+ # launcher version
13
+ class CLI < Thor
14
+
15
+ package_name "Clustr"
16
+
17
+ class_option :access_key_id, :type => :string
18
+ class_option :secret_access_key, :type => :string
19
+ class_option :region, :type => :string, :default => "eu-west-1"
20
+
21
+ def initialize(*args)
22
+ super
23
+ # Commit command line options to general config
24
+ Clustr::Config(options)
25
+
26
+ # Set AWS Configuration if it has been passed in
27
+ config = ["access_key_id", "secret_access_key", "region"]
28
+ ::AWS.config(options.select { |k| config.include?(k) })
29
+ describe_aws_configuration
30
+ end
31
+
32
+ desc "version", "Displays the current version number of Clustr."
33
+ # Displays the current version of the installed Clustr gem on the command line.
34
+ # For more help on this command, use `clustr help version` from the command line.
35
+ def version
36
+ Log.info Clustr::VERSION
37
+ end
38
+
39
+ desc "join", "Insert this node into the cluster, along with any additional attributes."
40
+ method_option :clusters, :type => :array, :desc => "List of clusters this node should join."
41
+ method_option :id, :type => :string, :desc => "ID of the node joining the cluster.", :required => true
42
+ method_option :attributes, :type => :hash, :desc => "Additional attributes to store for this node", :default => {}
43
+ # Joins the node that this command is ran on, to a new, or previously existing cluster.
44
+ # For more help on this command, use `clustr help join` from the command line.
45
+ def join
46
+ node = Node.new Config[:id], Config[:attributes]
47
+ (Config[:clusters] || clusters.keys).each do |cluster|
48
+ if clusters.include?(cluster)
49
+ if clusters[cluster].add(node)
50
+ Log.ok "Joined cluster #{cluster}"
51
+ end
52
+ else
53
+ raise ArgumentError.new "Cluster #{cluster} not configured."
54
+ end
55
+ end
56
+ end
57
+
58
+ desc "list", "Output only the unique identifiers for each node in a list."
59
+ method_option :cluster, :type => :string, :desc => "The cluster to output nodes from.", :required => true
60
+ method_option :delimiter, :type => :string, :desc => "The delimiter to separate each node identifier with, defaults to space", :default => " "
61
+ def list
62
+ puts clusters[Config[:cluster]].members.join(Config[:delimiter])
63
+ end
64
+
65
+ private
66
+
67
+ def clusters
68
+ @clusters ||= ClusterCollection.new(Config::Clusters.configuration)
69
+ end
70
+
71
+ def aws_configuration
72
+ AWS.config.credentials
73
+ end
74
+
75
+ def aws_configured?
76
+ begin
77
+ aws_configuration
78
+ rescue
79
+ false
80
+ end
81
+ end
82
+
83
+ def describe_aws_configuration
84
+ if aws_configured?
85
+ config = aws_configuration
86
+ Log.info "AWS Region #{config[:region]}"
87
+ Log.info "AWS Access Key #{config[:access_key_id]}"
88
+ else
89
+ Log.warn "No AWS config detected."
90
+ end
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,57 @@
1
+ module Clustr
2
+ # Represents a cluster that has been configured and initialized
3
+ # from the options passed into it. Each Cluster has an adapter,
4
+ # which is reponsible for determining the current members
5
+ # of a cluster, and adding new nodes.
6
+ class Cluster
7
+
8
+ attr_accessor :name
9
+
10
+ # Initializes a new Cluster from the options that
11
+ # are passed into it. Information about the adapter
12
+ # that this cluster should use is required.
13
+ #
14
+ # @example Instantiating a new Cluster
15
+ # cluster = Clustr::Cluster.new "my_cluster", {
16
+ # "adapter" => "SimpleDB",
17
+ # "key" => "simpledb-domain-edfvw2031"
18
+ # }
19
+ #
20
+ # @param [string] name of the cluster
21
+ # @param [hash] options to initialize the cluster with.
22
+ def initialize(name, options)
23
+ @name = name
24
+ @adapter = Clustr::Adapter.from_options(options)
25
+ end
26
+
27
+ # Return an array of the unique identifiers for each
28
+ # node within the cluster.
29
+ #
30
+ # @note This method only returns the ID's for each node,
31
+ # not any accompanying attributes for the node.
32
+ #
33
+ # @return [array] Cluster members, node ID's
34
+ def members
35
+ @adapter.members
36
+ end
37
+
38
+ # Returns an array of {Node} objects. These nodes
39
+ # have been identified as members of this Cluster.
40
+ #
41
+ # @return [array] Array of {Node} objects.
42
+ def nodes
43
+ raise NotImplementedError.new
44
+ end
45
+
46
+ # Add a node to this cluster. Note that the node
47
+ # must have a compatible ID.
48
+ #
49
+ # @param [Node] node to add to this cluster.
50
+ #
51
+ # @return [boolean] True if successful, false otherwise
52
+ def add(node)
53
+ @adapter.add node.id, node.created, node.properties
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,29 @@
1
+ module Clustr
2
+ # Represents a collection of the current {Cluster}
3
+ # configurations. This class can handle initializing
4
+ # clusters that have been defined through
5
+ # {Clustr::Config::Clusters}.
6
+ #
7
+ # @note The key for each cluster is its name, ie. "rabbitmq"
8
+ class ClusterCollection < Hash
9
+
10
+ # Initializes a collection of {Cluster} instances,
11
+ # by looping through the configurations that have been
12
+ # passed in.
13
+ #
14
+ # @example Initializing a ClusterCollection
15
+ # config = {
16
+ # "rabbitmq" => {
17
+ # "adapter" => "SimpleDB",
18
+ # "key" => "my-simpledb-domain"
19
+ # }
20
+ # }
21
+ # clusters = ClusterCollection.new(config)
22
+ def initialize(configurations = {})
23
+ configurations.each do |name, config|
24
+ self[name] = Clustr::Cluster.new(name, config)
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,56 @@
1
+ module Clustr
2
+ module Config
3
+ # Handles determining the configuration for clusters that are
4
+ # present on the machine. Note that configurations should
5
+ # be defined in "~/.clustr".
6
+ class Clusters < Hash
7
+
8
+ # Determines if there are any configured clusters.
9
+ #
10
+ # @return [boolean] True if one or more configuration
11
+ # are present, false otherwise.
12
+ def self.configured?
13
+ !configuration.empty?
14
+ end
15
+
16
+ # Returns the configuration for the clusters that have been
17
+ # defined inside the "~/.clustr/" folder. Note that the
18
+ # filename for each configuration, becomes the determined
19
+ # name for that cluster.
20
+ #
21
+ # @return [hash] A hash of the clusters and their accompanying
22
+ # configuration (including the adapter and key).
23
+ def self.configuration
24
+ clusters ||= self.new
25
+
26
+ config_files.each do |file|
27
+ cluster = Pathname.new(file).basename.to_s
28
+ clusters[cluster] = {}
29
+ File.open(file, 'r') do |fh|
30
+ fh.each_line do |line|
31
+ key, val = line.strip.split(%r(\s*=\s*))
32
+ clusters[cluster][key] = val if key && val
33
+ end
34
+ end
35
+ end
36
+
37
+ clusters
38
+ end
39
+
40
+ private
41
+
42
+ @@path = File.expand_path("~/.clustr")
43
+
44
+ def self.config_files
45
+ if Dir.exists?(@@path)
46
+ entries = Dir.entries(@@path)
47
+ entries = entries - [".", ".."]
48
+ entries.collect { |e| [@@path,e].join("/") }
49
+ else
50
+ []
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,71 @@
1
+ require 'clustr/config/clusters'
2
+
3
+ module Clustr
4
+
5
+ # Adds configuration values to the current static instance of Clustr.
6
+ #
7
+ # @overload self.Config(config={})
8
+ # @param [Hash] config of keys and values to add.
9
+ def self.Config(config={})
10
+ config.each do |key, value|
11
+ Clustr::Config[key] = value
12
+ end
13
+ end
14
+
15
+ # Controls all configuration settings for the Clustr gem.
16
+ # @note This module should be accessed from within a static context.
17
+ module Config
18
+
19
+ # Retrieves values for the provided keys.
20
+ # @note Keys not present within the configuration will be omitted from the returned hash.
21
+ #
22
+ # @example Guided Usage
23
+ # Clustr::Config.select(:a, :b, :c, :d)
24
+ #
25
+ # @param [Splat] args of the key value pairs to retrieve.
26
+ # @return [Hash] a hash of the keys present within configuration.
27
+ def self.select(*args)
28
+ @@_.select { |key| args.include?(key) }
29
+ end
30
+
31
+ # Accesses {Clustr::Config} as an array of key-based values.
32
+ #
33
+ # @example Retrieve a singular key using a symbol
34
+ # Clustr::Config[:a]
35
+ # @example Retrieve a singular key using a string
36
+ # Clustr::Config["a"]
37
+ #
38
+ # @param [Symbol] key of the value to retrieve.
39
+ # @return [Mixed, Nil] Will return the value of the key or nil.
40
+ def self.[](key)
41
+ @@_[key.to_sym]
42
+ end
43
+
44
+ # Sets or modifies a key and value within the current configuration state.
45
+ #
46
+ # @example Usage
47
+ # Clustr::Config[:a] = "hello world!"
48
+ #
49
+ # @param [Symbol] key of the value to set.
50
+ # @param [Mixed] value of the element.
51
+ # @return [Mixed] the new value of the element.
52
+ def self.[]=(key,value)
53
+ @@_[key.to_sym] = value
54
+ end
55
+
56
+ # Removes a key from the current configuration state.
57
+ #
58
+ # @example Usage
59
+ # Clustr::Config.delete!(:one, :or, :more, :keys)
60
+ #
61
+ # @param [Symbol] keys of the values to delete.
62
+ # @return [Hash] the values that have been deleted
63
+ def self.delete!(*keys)
64
+ keys.each { |k| @@_.delete(k.to_sym) }
65
+ end
66
+
67
+ private
68
+ @@_ = {}
69
+
70
+ end
71
+ end
data/lib/clustr/log.rb ADDED
@@ -0,0 +1,43 @@
1
+ require "logger"
2
+
3
+ module Clustr
4
+ # The Log class provides a static interface to output text to STDOUT.
5
+ # It provides helpful messages types such as debug, error and ok. On
6
+ # most command line interfaces, {Clustr::Log} will also output text
7
+ # from various messages types in an appropriate color.
8
+ #
9
+ # @example Sending a log message
10
+ # Clustr::Log.fatal "My fatal error is", exception.class.name
11
+ class Log
12
+
13
+ %w(debug error info warn fatal ok).each do |type|
14
+ define_singleton_method(type) do |*msgs|
15
+ @@log.send(type, *msgs.join(' '))
16
+ end
17
+ end
18
+
19
+ # Print an OK log message using a concatenation of various strings
20
+ # that have been passed in.
21
+ #
22
+ # @example Sending an OK Message
23
+ # Clustr::Log.ok "Hello", username # => "Hello David"
24
+ def self.ok(*msgs)
25
+ print format_msg("OK", *msgs.join(' ')), :ok
26
+ end
27
+
28
+ protected
29
+
30
+ def self.format_msg(severity, msg, datetime=Time.now)
31
+ "[#{datetime}] [#{severity}]: #{msg}\n"
32
+ end
33
+
34
+ private
35
+
36
+ @@log = Logger.new(STDOUT)
37
+
38
+ @@log.formatter = proc { |severity, datetime, progname, msg|
39
+ format_msg severity, msg, datetime
40
+ }
41
+
42
+ end
43
+ end