dotum 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.groc.json +6 -0
- data/.rspec +4 -0
- data/.simplecov +5 -0
- data/.travis.yml +15 -0
- data/CONTRIBUTING.md +63 -0
- data/Gemfile +59 -0
- data/Guardfile +30 -0
- data/MIT-LICENSE.md +21 -0
- data/README.md +24 -0
- data/Rakefile +15 -0
- data/bin/dotum +7 -0
- data/data/default_rules.dotum +44 -0
- data/dotum.gemspec +19 -0
- data/extern/json/CHANGES.md +9 -0
- data/extern/json/COPYING +58 -0
- data/extern/json/README.rdoc +358 -0
- data/extern/json/lib/json.rb +62 -0
- data/extern/json/lib/json/.DS_Store +0 -0
- data/extern/json/lib/json/add/bigdecimal.rb +28 -0
- data/extern/json/lib/json/add/complex.rb +22 -0
- data/extern/json/lib/json/add/core.rb +11 -0
- data/extern/json/lib/json/add/date.rb +34 -0
- data/extern/json/lib/json/add/date_time.rb +50 -0
- data/extern/json/lib/json/add/exception.rb +31 -0
- data/extern/json/lib/json/add/ostruct.rb +31 -0
- data/extern/json/lib/json/add/range.rb +29 -0
- data/extern/json/lib/json/add/rational.rb +22 -0
- data/extern/json/lib/json/add/regexp.rb +30 -0
- data/extern/json/lib/json/add/struct.rb +30 -0
- data/extern/json/lib/json/add/symbol.rb +25 -0
- data/extern/json/lib/json/add/time.rb +38 -0
- data/extern/json/lib/json/common.rb +487 -0
- data/extern/json/lib/json/generic_object.rb +70 -0
- data/extern/json/lib/json/pure.rb +21 -0
- data/extern/json/lib/json/pure/generator.rb +522 -0
- data/extern/json/lib/json/pure/parser.rb +359 -0
- data/extern/json/lib/json/version.rb +8 -0
- data/lib/dotum.rb +11 -0
- data/lib/dotum/abstract_rules.rb +5 -0
- data/lib/dotum/abstract_rules/base.rb +56 -0
- data/lib/dotum/abstract_rules/globbable_files.rb +51 -0
- data/lib/dotum/abstract_rules/options_base.rb +33 -0
- data/lib/dotum/autoload_convention.rb +34 -0
- data/lib/dotum/cli.rb +35 -0
- data/lib/dotum/context.rb +55 -0
- data/lib/dotum/externs/json.rb +9 -0
- data/lib/dotum/logger.rb +50 -0
- data/lib/dotum/options_context.rb +21 -0
- data/lib/dotum/rule_dsl.rb +73 -0
- data/lib/dotum/rule_options_dsl.rb +116 -0
- data/lib/dotum/rule_runner.rb +16 -0
- data/lib/dotum/rules.rb +5 -0
- data/lib/dotum/rules/cd.rb +23 -0
- data/lib/dotum/rules/download.rb +32 -0
- data/lib/dotum/rules/link.rb +22 -0
- data/lib/dotum/rules/repo.rb +65 -0
- data/lib/dotum/rules/require_extension.rb +42 -0
- data/lib/dotum/rules/run.rb +23 -0
- data/lib/dotum/rules/use.rb +33 -0
- data/lib/dotum/rules/use_repo.rb +58 -0
- data/lib/dotum/standard_options.rb +5 -0
- data/lib/dotum/standard_options/destination.rb +22 -0
- data/lib/dotum/util.rb +5 -0
- data/lib/dotum/util/ansi_colors.rb +26 -0
- data/lib/dotum/util/path.rb +120 -0
- data/lib/dotum/version.rb +5 -0
- data/spec/fixtures/autoload_convention/abc_one_two_three.rb +7 -0
- data/spec/fixtures/autoload_convention/allcaps.rb +7 -0
- data/spec/fixtures/autoload_convention/mismatched.rb +3 -0
- data/spec/fixtures/autoload_convention/multi_token.rb +7 -0
- data/spec/fixtures/autoload_convention/single.rb +7 -0
- data/spec/fixtures/autoload_convention/string.rb +7 -0
- data/spec/spec_helper.rb +76 -0
- data/spec/unit/dotum/autoload_convention/const_missing_spec.rb +57 -0
- data/tasks/console.rake +9 -0
- data/tasks/spec.rake +7 -0
- data/tasks/spec/ci.rake +16 -0
- data/tasks/spec/coverage.rake +19 -0
- data/tasks/spec/mutate.rake +71 -0
- metadata +123 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Dotum::AbstractRules::OptionsBase < Dotum::AbstractRules::Base
|
4
|
+
|
5
|
+
def self.exec(context, *args, &block)
|
6
|
+
options = option_defaults.merge(expand_shorthand(*args))
|
7
|
+
options.merge! eval_options_block(&block) if block
|
8
|
+
|
9
|
+
if errors = validate_options(options)
|
10
|
+
raise "Validation errors: #{errors.inspect}"
|
11
|
+
end
|
12
|
+
|
13
|
+
if respond_to? :expand_options
|
14
|
+
expand_options(context, options).map { |rule_options|
|
15
|
+
new(context, rule_options).exec
|
16
|
+
}
|
17
|
+
else
|
18
|
+
new(context, options).exec
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(context, options)
|
23
|
+
super(context)
|
24
|
+
|
25
|
+
options.each do |option, value|
|
26
|
+
filter = self.class.option_configs[option][:filter]
|
27
|
+
value = instance_exec(value, &filter) if filter && !value.nil?
|
28
|
+
|
29
|
+
instance_variable_set(:"@#{option}", value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Autoload Convention
|
4
|
+
# ===================
|
5
|
+
# We adhere to a strict convention for the constants in this library:
|
6
|
+
#
|
7
|
+
# `Camel::Caps::BasedConstants` map to their underscore variants of
|
8
|
+
# `camel/caps/based_constants`.
|
9
|
+
#
|
10
|
+
# Each autoloadable parent module/class only needs to to `extend` the
|
11
|
+
# `AutoloadConvention` to bootstrap this behavior.
|
12
|
+
module Dotum
|
13
|
+
module AutoloadConvention
|
14
|
+
|
15
|
+
# `autoload` is dead, and we don't want to deal with its removal in 2.0,
|
16
|
+
# so here's a thread-unsafe poor man's solution.
|
17
|
+
def const_missing(sym)
|
18
|
+
full_sym = "#{name}::#{sym}"
|
19
|
+
path_parts = full_sym.split("::").map { |part|
|
20
|
+
part.gsub! /([^A-Z])([A-Z]+)/, "\\1_\\2" # OneTwo -> One_Two
|
21
|
+
part.gsub! /([A-Z]+)([A-Z][^A-Z]+)/, "\\1_\\2" # ABCOne -> ABC_One
|
22
|
+
|
23
|
+
part.downcase
|
24
|
+
}
|
25
|
+
|
26
|
+
require File.join(path_parts)
|
27
|
+
|
28
|
+
# Make sure that we don't get stuck in an endless loop. `const_get` calls
|
29
|
+
# into `const_missing`, so we can't use that.
|
30
|
+
eval "defined?(#{full_sym}) ? #{full_sym} : super"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
data/lib/dotum/cli.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Dotum::CLI
|
4
|
+
|
5
|
+
def self.exec(args)
|
6
|
+
new(args).exec!
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(args)
|
10
|
+
@args = args
|
11
|
+
|
12
|
+
@state_dir = Dotum::Util::Path.new("~/.dotum")
|
13
|
+
@target_dir = Dotum::Util::Path.new("~")
|
14
|
+
@package_dir = Dotum::Util::Path.new("~/.dotum/local")
|
15
|
+
end
|
16
|
+
|
17
|
+
def exec!
|
18
|
+
context = Dotum::Context.new(
|
19
|
+
# :no_remote => true,
|
20
|
+
:state_dir => @state_dir,
|
21
|
+
:target_dir => @target_dir,
|
22
|
+
:logger => Dotum::Logger.new
|
23
|
+
)
|
24
|
+
|
25
|
+
unless @package_dir.directory?
|
26
|
+
puts
|
27
|
+
puts "#{@package_dir.pretty} doesn't exist! Creating for now..."
|
28
|
+
@package_dir.mkpath!
|
29
|
+
end
|
30
|
+
|
31
|
+
Dotum::Rules::UseRepo.exec(context, @package_dir)
|
32
|
+
print "\n\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Dotum::Context
|
4
|
+
|
5
|
+
def initialize(attributes={})
|
6
|
+
set_attributes(attributes)
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :package_dir
|
10
|
+
attr_reader :target_dir
|
11
|
+
attr_reader :state_dir
|
12
|
+
|
13
|
+
attr_reader :logger
|
14
|
+
attr_reader :depth
|
15
|
+
|
16
|
+
def no_remote?
|
17
|
+
@no_remote
|
18
|
+
end
|
19
|
+
|
20
|
+
def attributes
|
21
|
+
result = {}
|
22
|
+
instance_variables.each do |var_name|
|
23
|
+
result[var_name[1..-1].to_sym] = instance_variable_get(var_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
result
|
27
|
+
end
|
28
|
+
|
29
|
+
def fork(new_attributes={})
|
30
|
+
self.class.new(attributes.merge(new_attributes || {}))
|
31
|
+
end
|
32
|
+
|
33
|
+
def child(new_attributes={})
|
34
|
+
new_attributes ||= {}
|
35
|
+
new_attributes[:depth] = (@depth || 0) + 1
|
36
|
+
|
37
|
+
fork(new_attributes)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def set_attributes(attributes)
|
43
|
+
attributes.each_pair do |key, value|
|
44
|
+
case key
|
45
|
+
when :package_dir then @package_dir = Dotum::Util::Path.new(value)
|
46
|
+
when :target_dir then @target_dir = Dotum::Util::Path.new(value)
|
47
|
+
when :state_dir then @state_dir = Dotum::Util::Path.new(value)
|
48
|
+
when :logger then @logger = value
|
49
|
+
when :no_remote then @no_remote = value
|
50
|
+
when :depth then @depth = value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
data/lib/dotum/logger.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Dotum::Logger
|
4
|
+
include Dotum::Util::ANSIColors
|
5
|
+
|
6
|
+
INDENT = " "
|
7
|
+
|
8
|
+
def start_rule(rule)
|
9
|
+
@last_rule = rule
|
10
|
+
@last_rule_start = Time.now
|
11
|
+
|
12
|
+
print "\n"
|
13
|
+
print rule_line(rule)
|
14
|
+
end
|
15
|
+
|
16
|
+
def finish_rule(rule, status, reason)
|
17
|
+
message = "#{rule_line(rule)} - #{status.inspect} - #{reason.inspect}"
|
18
|
+
if @last_rule == rule
|
19
|
+
delta = (Time.now - @last_rule_start) * 1000.0
|
20
|
+
message += " (%.2fms)" % delta
|
21
|
+
|
22
|
+
print "\r"
|
23
|
+
else
|
24
|
+
print "\n\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
print message
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def rule_line(rule)
|
33
|
+
indent = INDENT * rule.context.depth
|
34
|
+
action = rule.class.pretty
|
35
|
+
subject = rule.pretty_subject
|
36
|
+
|
37
|
+
action = c_cyan(action)
|
38
|
+
subject = colorize_subject(subject)
|
39
|
+
|
40
|
+
"#{indent}#{action}: #{subject}"
|
41
|
+
end
|
42
|
+
|
43
|
+
SUBJECT_MATCHER = /^(.*?)( \(.+\))?$/
|
44
|
+
def colorize_subject(subject)
|
45
|
+
match = SUBJECT_MATCHER.match(subject)
|
46
|
+
|
47
|
+
"#{c_magenta(match[1])}#{match[2]}"
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
class Dotum::OptionsContext < Hash
|
6
|
+
|
7
|
+
def initialize(option_configs)
|
8
|
+
@known_options = Set.new(option_configs.keys)
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(sym, *args)
|
12
|
+
super if args.size != 1
|
13
|
+
|
14
|
+
unless @known_options.include? sym
|
15
|
+
raise ArgumentError, "Unknown option '#{sym}'"
|
16
|
+
end
|
17
|
+
|
18
|
+
self[sym] = args[0]
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "rbconfig"
|
4
|
+
|
5
|
+
module Dotum::RuleDSL
|
6
|
+
|
7
|
+
def success!(reason=nil)
|
8
|
+
throw :finish_rule, [:success, reason]
|
9
|
+
end
|
10
|
+
|
11
|
+
def failure!(reason=nil)
|
12
|
+
throw :finish_rule, [:failure, reason]
|
13
|
+
end
|
14
|
+
|
15
|
+
def skip!(reason=nil)
|
16
|
+
throw :finish_rule, [:skip, reason]
|
17
|
+
end
|
18
|
+
|
19
|
+
def platform?(name)
|
20
|
+
os = RbConfig::CONFIG["host_os"].downcase
|
21
|
+
|
22
|
+
case name
|
23
|
+
when /os\s?x/i then /mac|darwin/ === os
|
24
|
+
when /win(dows)?/i then /mswin|win|mingw/ === os
|
25
|
+
when /linux/i then /linux|cygwin/ === os
|
26
|
+
when /bsd/i then /bsd/ === os
|
27
|
+
when /solaris/i then /solaris|sunos/ === os
|
28
|
+
else
|
29
|
+
if name.is_a? Regexp
|
30
|
+
name === os
|
31
|
+
else
|
32
|
+
Regexp.new(name, "i") === os
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def available?(command)
|
38
|
+
# TODO: Windows friendly.
|
39
|
+
`which "#{command}"`
|
40
|
+
$?.success?
|
41
|
+
end
|
42
|
+
|
43
|
+
def package_dir
|
44
|
+
context.package_dir
|
45
|
+
end
|
46
|
+
|
47
|
+
def target_dir
|
48
|
+
context.target_dir
|
49
|
+
end
|
50
|
+
|
51
|
+
def state_dir
|
52
|
+
context.state_dir
|
53
|
+
end
|
54
|
+
|
55
|
+
def method_missing(sym, *args, &block)
|
56
|
+
rule_class_name = sym.to_s.split("_").map(&:capitalize).join.to_sym
|
57
|
+
|
58
|
+
begin
|
59
|
+
rule_class = Dotum::Rules.const_get(rule_class_name)
|
60
|
+
rescue LoadError
|
61
|
+
raise NoMethodError, "Unknown rule #{sym}. Tried to load Dotum::Rules::#{rule_class_name}: #{$!.message}"
|
62
|
+
end
|
63
|
+
|
64
|
+
Dotum::RuleDSL.module_eval <<-end_eval, __FILE__, __LINE__
|
65
|
+
def #{sym}(*args, &block)
|
66
|
+
Dotum::Rules::#{rule_class_name}.exec(context, *args, &block)
|
67
|
+
end
|
68
|
+
end_eval
|
69
|
+
|
70
|
+
send(sym, *args, &block)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Dotum::RuleOptionsDSL
|
4
|
+
|
5
|
+
OptionConfig = Struct.new(:filter, :validator, :default)
|
6
|
+
|
7
|
+
|
8
|
+
# DSL
|
9
|
+
# ---
|
10
|
+
|
11
|
+
def shorthand(*args)
|
12
|
+
@shorthand_config ||= args
|
13
|
+
end
|
14
|
+
|
15
|
+
def required(option, &block)
|
16
|
+
(@option_configs ||= {})[option] = OptionConfig.new(
|
17
|
+
block,
|
18
|
+
proc { |v| "Option '#{option}' is required." if v.nil? }
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def optional(option, default=nil, &block)
|
23
|
+
(@option_configs ||= {})[option] = OptionConfig.new(block, nil, default)
|
24
|
+
end
|
25
|
+
|
26
|
+
def standard(option)
|
27
|
+
option_module_name = option.to_s.split("_").map(&:capitalize).join.to_sym
|
28
|
+
|
29
|
+
begin
|
30
|
+
option_module = Dotum::StandardOptions.const_get(option_module_name)
|
31
|
+
rescue LoadError
|
32
|
+
raise "Unknown standard option '#{option}'. Tried to load Dotum::StandardOptions::#{option_module_name}: #{$!.message}"
|
33
|
+
end
|
34
|
+
|
35
|
+
module_configs = option_module.instance_variable_get(:@option_configs)
|
36
|
+
unless module_configs && module_configs[option]
|
37
|
+
raise "Dotum::StandardOptions::#{option_module_name} is misconfigured; expected it to define the option '#{option}'"
|
38
|
+
end
|
39
|
+
|
40
|
+
include option_module
|
41
|
+
end
|
42
|
+
|
43
|
+
def register_preprocessor(sym)
|
44
|
+
@preprocessors ||= []
|
45
|
+
@preprocessors.push(sym)
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# Configuration Management
|
50
|
+
# ------------------------
|
51
|
+
|
52
|
+
def option_configs
|
53
|
+
@memoized_option_configs ||= ancestors.reverse_each.reduce({}) { |result, ancestor|
|
54
|
+
result.merge(ancestor.instance_variable_get(:@option_configs) || {})
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def option_defaults
|
59
|
+
@memoized_option_defaults ||= {}.tap do |defaults|
|
60
|
+
option_configs.each do |option, config|
|
61
|
+
defaults[option] = config[:default] unless config[:default].nil?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def preprocessor_methods
|
67
|
+
@memoized_preprocessor_methods ||= ancestors.map { |ancestor|
|
68
|
+
ancestor.instance_variable_get(:@preprocessors)
|
69
|
+
}.compact.flatten
|
70
|
+
end
|
71
|
+
|
72
|
+
def shorthand_config
|
73
|
+
return @shorthand_config if defined? @shorthand_config
|
74
|
+
return superclass.shorthand_config if superclass.respond_to? :shorthand_config
|
75
|
+
|
76
|
+
[]
|
77
|
+
end
|
78
|
+
|
79
|
+
def expand_shorthand(*args)
|
80
|
+
result = {}
|
81
|
+
shorthand_config.zip(args).each do |config, value|
|
82
|
+
case config
|
83
|
+
when Symbol then result[config] = value
|
84
|
+
when Hash
|
85
|
+
if value.is_a? Hash
|
86
|
+
result[config.keys.first] = value.keys.first
|
87
|
+
result[config.values.first] = value.values.first
|
88
|
+
else
|
89
|
+
result[config.keys.first] = value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
result
|
95
|
+
end
|
96
|
+
|
97
|
+
def eval_options_block(&block)
|
98
|
+
options = Dotum::OptionsContext.new(option_configs)
|
99
|
+
options.instance_eval(&block)
|
100
|
+
|
101
|
+
options
|
102
|
+
end
|
103
|
+
|
104
|
+
def validate_options(options)
|
105
|
+
errors = []
|
106
|
+
option_configs.each do |option, config|
|
107
|
+
next unless validator = config[:validator]
|
108
|
+
next unless error = validator.call(options[option])
|
109
|
+
|
110
|
+
errors.push(error)
|
111
|
+
end
|
112
|
+
|
113
|
+
errors.size > 0 ? errors : nil
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Dotum::RuleRunner
|
4
|
+
include Dotum::RuleDSL
|
5
|
+
|
6
|
+
def self.eval(context, *args, &block)
|
7
|
+
new(context).instance_eval(*args, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(context)
|
11
|
+
@context = context
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :context
|
15
|
+
|
16
|
+
end
|