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
data/lib/dotum/rules.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Dotum::Rules::Cd < Dotum::AbstractRules::Base
|
4
|
+
|
5
|
+
# Special case; we don't accept an options block.
|
6
|
+
def initialize(context, destination, &block)
|
7
|
+
super(context)
|
8
|
+
|
9
|
+
@destination = @context.target_dir.join(destination)
|
10
|
+
@block = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def pretty_subject
|
14
|
+
@destination.pretty
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def execute
|
20
|
+
Dir.chdir(@destination, &@block)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Dotum::Rules::Download < Dotum::AbstractRules::OptionsBase
|
4
|
+
|
5
|
+
shorthand :uri => :destination
|
6
|
+
|
7
|
+
required :uri
|
8
|
+
standard :destination
|
9
|
+
optional :mode
|
10
|
+
optional :owner
|
11
|
+
optional :group
|
12
|
+
|
13
|
+
def pretty_subject
|
14
|
+
"#{@destination.pretty} (#{@uri})"
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def execute
|
20
|
+
if @destination.exists? && !@destination.file?
|
21
|
+
failure! "#{@destination} already exists and is not a file!"
|
22
|
+
end
|
23
|
+
|
24
|
+
# TODO: ETag support!
|
25
|
+
run "curl", "--output", @destination, @uri
|
26
|
+
|
27
|
+
run "chmod", @mode, @destination if @mode
|
28
|
+
run "chown", @owner, @destination if @owner
|
29
|
+
run "chgrp", @group, @destination if @group
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Dotum::Rules::Link < Dotum::AbstractRules::GlobbableFiles
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def execute
|
8
|
+
link_path = @destination.symlink? ? @destination.link_path : nil
|
9
|
+
|
10
|
+
success! "Already linked" if link_path == @source
|
11
|
+
if @destination.exists?
|
12
|
+
if link_path
|
13
|
+
failure! "Target already exists as a symlink to #{link_path}"
|
14
|
+
else
|
15
|
+
failure! "Target already exists"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
run "ln", "-s", @source, @destination
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Dotum::Rules::Repo < Dotum::AbstractRules::OptionsBase
|
4
|
+
|
5
|
+
shorthand :repo_uri => :destination
|
6
|
+
|
7
|
+
required :repo_uri
|
8
|
+
standard :destination
|
9
|
+
optional :branch
|
10
|
+
|
11
|
+
attr_reader :destination
|
12
|
+
|
13
|
+
def pretty_subject
|
14
|
+
@repo_uri
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def execute
|
20
|
+
@destination ||= default_destination
|
21
|
+
@repo_exists = @destination.join(".git").directory?
|
22
|
+
|
23
|
+
if !is_local? && context.no_remote?
|
24
|
+
if @repo_exists
|
25
|
+
skip! "Remote interactions are disabled."
|
26
|
+
else
|
27
|
+
failure! "Remote interactions are disabled; cannot clone!"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
if @repo_exists
|
32
|
+
update_repo
|
33
|
+
else
|
34
|
+
clone_repo
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def clone_repo
|
39
|
+
command = ["git", "clone"]
|
40
|
+
command += ["--branch", @branch] if @branch
|
41
|
+
command += [@repo_uri, @destination]
|
42
|
+
|
43
|
+
run *command
|
44
|
+
end
|
45
|
+
|
46
|
+
def update_repo
|
47
|
+
cd @destination do
|
48
|
+
run "git", "add", "--all"
|
49
|
+
run "git", "commit", "--quiet", "--allow-empty", "--message", "Temporary Dotum Commit"
|
50
|
+
run "git", "pull", "--rebase"
|
51
|
+
run "git", "reset", "HEAD^"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def default_destination
|
56
|
+
basename = [@repo_uri, @branch].compact.join("-").gsub(/[\/:]+/, "-")
|
57
|
+
|
58
|
+
context.state_dir.join("repo", basename)
|
59
|
+
end
|
60
|
+
|
61
|
+
def is_local?
|
62
|
+
@repo_uri.start_with? "file://"
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
class Dotum::Rules::RequireExtension < Dotum::AbstractRules::OptionsBase
|
6
|
+
|
7
|
+
shorthand :extension_uri
|
8
|
+
|
9
|
+
required :extension_uri
|
10
|
+
optional :legacy
|
11
|
+
optional :branch
|
12
|
+
|
13
|
+
def pretty_subject
|
14
|
+
@extension_uri
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def execute
|
20
|
+
# Does it look like a URI?
|
21
|
+
if URI.parse(@extension_uri).scheme
|
22
|
+
rule = repo @extension_uri, {:branch => @branch}
|
23
|
+
@destination = rule.destination
|
24
|
+
# Nope, we're just treating a local directory like a repo.
|
25
|
+
else
|
26
|
+
@destination = Dotum::Util::Path.new(@extension_uri)
|
27
|
+
end
|
28
|
+
|
29
|
+
failure! "Failed to clone repo to #{@destination}" unless @destination.directory?
|
30
|
+
|
31
|
+
# Dotum extensions are practically gems, but we don't want the overhead of
|
32
|
+
# building a gemspec. Also, `require_extension` is the only supported
|
33
|
+
# means of loading them. We don't want magic extensions to be already
|
34
|
+
# loaded via gems. Explicit is good here!
|
35
|
+
|
36
|
+
# So, just load all files in the extension and be done with it.
|
37
|
+
@destination.glob("lib/**/*.rb").each do |path|
|
38
|
+
Kernel.load(path)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Dotum::Rules::Run < Dotum::AbstractRules::Base
|
4
|
+
|
5
|
+
def initialize(context, *command)
|
6
|
+
super(context)
|
7
|
+
|
8
|
+
@command = command
|
9
|
+
end
|
10
|
+
|
11
|
+
def pretty_subject
|
12
|
+
@command.join(" ")
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def execute
|
18
|
+
Kernel.system(*@command)
|
19
|
+
|
20
|
+
failure! unless $?.success?
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Dotum::Rules::Use < Dotum::AbstractRules::OptionsBase
|
4
|
+
|
5
|
+
shorthand :path
|
6
|
+
|
7
|
+
required(:path) { |v| package_dir.join(v) }
|
8
|
+
|
9
|
+
def self.expand_options(context, options)
|
10
|
+
paths = context.package_dir.glob(options[:path], &:directory?)
|
11
|
+
paths.reject! &:hidden?
|
12
|
+
|
13
|
+
paths.map { |p| options.merge(:path => p) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def pretty_subject
|
17
|
+
@path.pretty
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def execute
|
23
|
+
failure! "Expected #{@path} to be a directory!" unless @path.directory?
|
24
|
+
|
25
|
+
rule_file = @path.join("rules.dotum")
|
26
|
+
rule_file = Dotum::DATA_PATH.join("default_rules.dotum") unless rule_file.file?
|
27
|
+
|
28
|
+
new_context = context.fork(:package_dir => @path)
|
29
|
+
|
30
|
+
Dotum::RuleRunner.eval(new_context, rule_file.read, rule_file)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
class Dotum::Rules::UseRepo < Dotum::AbstractRules::OptionsBase
|
6
|
+
|
7
|
+
shorthand :repo_uri
|
8
|
+
|
9
|
+
required :repo_uri
|
10
|
+
optional :legacy
|
11
|
+
optional :branch
|
12
|
+
|
13
|
+
def pretty_subject
|
14
|
+
@repo_uri
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def execute
|
20
|
+
# Does it look like a URI?
|
21
|
+
if URI.parse(@repo_uri).scheme
|
22
|
+
rule = repo @repo_uri, {:branch => @branch}
|
23
|
+
@destination = rule.destination
|
24
|
+
# Nope, we're just treating a local directory like a repo.
|
25
|
+
else
|
26
|
+
@destination = Dotum::Util::Path.new(@repo_uri)
|
27
|
+
end
|
28
|
+
|
29
|
+
failure! "Failed to clone repo to #{@destination}" unless @destination.directory?
|
30
|
+
# We are now located within the repo.
|
31
|
+
@context = context.fork(:package_dir => @destination)
|
32
|
+
|
33
|
+
if @legacy
|
34
|
+
execute_legacy
|
35
|
+
else
|
36
|
+
execute_dotum
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def execute_dotum
|
41
|
+
# Straight up `use` the repo if it is rooted with a `rules.dotum`.
|
42
|
+
if @destination.join('rules.dotum').file?
|
43
|
+
use "."
|
44
|
+
|
45
|
+
# Otherwise, this is a 'meta-package' that contains child packages.
|
46
|
+
else
|
47
|
+
@destination.relative_glob("*", &:directory?).each do |package_path|
|
48
|
+
use package_path
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute_legacy
|
54
|
+
# In legacy mode, we just symlink everything
|
55
|
+
link "**/*"
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Dotum::StandardOptions::Destination
|
4
|
+
extend Dotum::RuleOptionsDSL
|
5
|
+
|
6
|
+
optional(:destination) { |v| context.target_dir.join(v) }
|
7
|
+
|
8
|
+
def preprocess_for_destination
|
9
|
+
# It's required by the time you reach the preprocessing step; but not
|
10
|
+
# necessarily required for the shorthand.
|
11
|
+
failure! "Option 'destination' is required." if @destination.nil?
|
12
|
+
|
13
|
+
parent_dir = @destination.dirname
|
14
|
+
if parent_dir.exists? && !parent_dir.directory?
|
15
|
+
failure! "Parent path #{parent_dir} is not a directory; cannot write into it!"
|
16
|
+
end
|
17
|
+
|
18
|
+
run "mkdir", "-p", parent_dir unless parent_dir.directory?
|
19
|
+
end
|
20
|
+
register_preprocessor :preprocess_for_destination
|
21
|
+
|
22
|
+
end
|
data/lib/dotum/util.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Dotum::Util::ANSIColors
|
4
|
+
|
5
|
+
def ansi_color(text, code)
|
6
|
+
"\e[#{code}m#{text}\e[0m"
|
7
|
+
end
|
8
|
+
|
9
|
+
colors = [:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white]
|
10
|
+
colors.each_with_index do |color, i|
|
11
|
+
color_code = 30 + i
|
12
|
+
|
13
|
+
module_eval <<-end_eval, __FILE__, __LINE__
|
14
|
+
|
15
|
+
def c_#{color}(text)
|
16
|
+
ansi_color(text, "#{color_code}")
|
17
|
+
end
|
18
|
+
|
19
|
+
def c_bright_#{color}(text)
|
20
|
+
ansi_color(text, "#{color_code};1")
|
21
|
+
end
|
22
|
+
|
23
|
+
end_eval
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
# **Very** similar to `Pathname`.
|
6
|
+
class Dotum::Util::Path
|
7
|
+
|
8
|
+
ABSOLUTE_PATH_MATCHER = /^(~?\/|\w+\:\\)/
|
9
|
+
|
10
|
+
def initialize(path, relative=nil)
|
11
|
+
path = path.to_str.gsub(/[\/\\]/, File::Separator)
|
12
|
+
|
13
|
+
@path = File.expand_path(path, relative)
|
14
|
+
end
|
15
|
+
|
16
|
+
def join(*components)
|
17
|
+
(components.size - 1).downto(0) do |i|
|
18
|
+
# Do we have absolute paths? Stop at the last one.
|
19
|
+
if components[i].to_str =~ ABSOLUTE_PATH_MATCHER
|
20
|
+
return self.class.new(File.join(*components[i..-1]))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
self.class.new(File.join(@path, *components))
|
25
|
+
end
|
26
|
+
|
27
|
+
def exists?
|
28
|
+
File.exist? @path
|
29
|
+
end
|
30
|
+
|
31
|
+
def file?
|
32
|
+
File.file? @path
|
33
|
+
end
|
34
|
+
|
35
|
+
def directory?
|
36
|
+
File.directory? @path
|
37
|
+
end
|
38
|
+
|
39
|
+
def symlink?
|
40
|
+
File.symlink? @path
|
41
|
+
end
|
42
|
+
|
43
|
+
def hidden?
|
44
|
+
File.split(@path).any? { |p| p.start_with? "." }
|
45
|
+
end
|
46
|
+
|
47
|
+
def link_path
|
48
|
+
self.class.new(File.readlink(@path))
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_s
|
52
|
+
@path
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_str
|
56
|
+
@path
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.home_dir
|
60
|
+
@home_dir ||= File.expand_path("~")
|
61
|
+
end
|
62
|
+
|
63
|
+
def pretty
|
64
|
+
home_dir = File.expand_path("~")
|
65
|
+
if @path.start_with? home_dir
|
66
|
+
return "~" + @path[home_dir.size..-1]
|
67
|
+
end
|
68
|
+
|
69
|
+
@path
|
70
|
+
end
|
71
|
+
|
72
|
+
def read
|
73
|
+
File.read(@path)
|
74
|
+
end
|
75
|
+
|
76
|
+
def write(content)
|
77
|
+
File.open(@path, "w") do |file|
|
78
|
+
file.write(content)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def dirname
|
83
|
+
self.class.new(File.dirname(@path))
|
84
|
+
end
|
85
|
+
|
86
|
+
def basename
|
87
|
+
File.basename(@path)
|
88
|
+
end
|
89
|
+
|
90
|
+
def glob(expression, &filter)
|
91
|
+
matches = Dir.glob(join(expression), File::FNM_DOTMATCH)
|
92
|
+
|
93
|
+
if filter
|
94
|
+
matches.reject! { |p| !filter.call(self.class.new(p)) }
|
95
|
+
end
|
96
|
+
|
97
|
+
matches.reject! { |path|
|
98
|
+
basename = File.basename(path)
|
99
|
+
|
100
|
+
basename == "." || basename == ".."
|
101
|
+
}
|
102
|
+
|
103
|
+
matches.map { |p| self.class.new(p) }
|
104
|
+
end
|
105
|
+
|
106
|
+
def relative_glob(expression, &filter)
|
107
|
+
glob(expression, &filter).map { |path|
|
108
|
+
path.to_str[(@path.size + 1)..-1] || "."
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
def ==(other)
|
113
|
+
to_str == other.to_str
|
114
|
+
end
|
115
|
+
|
116
|
+
def mkpath!
|
117
|
+
FileUtils.mkpath(@path)
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|