dotum 0.0.0
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 +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
|