clustr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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