roger 1.1.3 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.hound.yml +2 -0
- data/.rubocop.yml +47 -0
- data/.travis.yml +1 -5
- data/CHANGELOG.md +8 -0
- data/Gemfile +3 -3
- data/Rakefile +10 -4
- data/bin/roger +1 -1
- data/doc/mockupfile.md +97 -0
- data/doc/templating.md +5 -1
- data/examples/default_template/Gemfile +1 -1
- data/lib/roger/cli.rb +41 -36
- data/lib/roger/cli/command.rb +2 -4
- data/lib/roger/cli/generate.rb +1 -0
- data/lib/roger/cli/release.rb +2 -2
- data/lib/roger/cli/serve.rb +11 -11
- data/lib/roger/cli/test.rb +6 -5
- data/lib/roger/extractor.rb +42 -43
- data/lib/roger/generators.rb +27 -19
- data/lib/roger/generators/generator.rb +7 -10
- data/lib/roger/generators/new.rb +56 -41
- data/lib/roger/generators/templates/generator.tt +5 -5
- data/lib/roger/helpers/get_callable.rb +15 -14
- data/lib/roger/helpers/logging.rb +35 -13
- data/lib/roger/mockupfile.rb +13 -23
- data/lib/roger/project.rb +41 -34
- data/lib/roger/rack/roger.rb +28 -29
- data/lib/roger/rack/sleep.rb +4 -5
- data/lib/roger/release.rb +95 -72
- data/lib/roger/release/cleaner.rb +14 -13
- data/lib/roger/release/finalizers.rb +10 -10
- data/lib/roger/release/finalizers/dir.rb +17 -19
- data/lib/roger/release/finalizers/git_branch.rb +76 -38
- data/lib/roger/release/finalizers/rsync.rb +60 -49
- data/lib/roger/release/finalizers/zip.rb +32 -29
- data/lib/roger/release/injector.rb +43 -37
- data/lib/roger/release/processors.rb +24 -22
- data/lib/roger/release/processors/mockup.rb +97 -69
- data/lib/roger/release/processors/url_relativizer.rb +57 -30
- data/lib/roger/release/scm.rb +30 -27
- data/lib/roger/release/scm/git.rb +101 -92
- data/lib/roger/resolver.rb +86 -61
- data/lib/roger/server.rb +52 -27
- data/lib/roger/template.rb +102 -74
- data/lib/roger/test.rb +16 -13
- data/lib/roger/version.rb +3 -2
- data/roger.gemspec +9 -5
- data/test/helpers/cli.rb +17 -15
- data/test/project/Gemfile +2 -2
- data/test/project/html/formats/csv.rcsv +0 -0
- data/test/project/lib/generators/test.rb +2 -3
- data/test/project/lib/tests/fail/fail.rb +5 -6
- data/test/project/lib/tests/noop/lib/cli.rb +2 -1
- data/test/project/lib/tests/noop/lib/test.rb +5 -5
- data/test/project/lib/tests/noop/noop.rb +2 -1
- data/test/project/lib/tests/succeed/succeed.rb +5 -6
- data/test/unit/cli/cli_base_test.rb +2 -3
- data/test/unit/cli/cli_generate_test.rb +9 -10
- data/test/unit/cli/cli_serve_test.rb +22 -18
- data/test/unit/cli/cli_test_test.rb +13 -15
- data/test/unit/cli/cli_version_test.rb +4 -4
- data/test/unit/generators_test.rb +8 -10
- data/test/unit/helpers/logging_test.rb +64 -0
- data/test/unit/rack/roger_test.rb +21 -0
- data/test/unit/release/cleaner_test.rb +23 -19
- data/test/unit/release/finalizers/git_branch_test.rb +2 -1
- data/test/unit/release/finalizers/zip_test.rb +48 -0
- data/test/unit/release/mockup_test.rb +48 -0
- data/test/unit/release/processors_test.rb +19 -19
- data/test/unit/release_test.rb +15 -14
- data/test/unit/resolver_test.rb +21 -14
- data/test/unit/server_test.rb +31 -0
- data/test/unit/template_test.rb +58 -36
- data/test/unit/test_test.rb +3 -2
- metadata +35 -9
- data/test/Mockupfile-syntax.rb +0 -93
@@ -1,44 +1,47 @@
|
|
1
1
|
module Roger::Release::Finalizers
|
2
|
-
|
2
|
+
# The zip finalizer
|
3
|
+
# The zip finalizer will
|
3
4
|
class Zip < Base
|
4
|
-
|
5
5
|
attr_reader :release
|
6
|
-
|
6
|
+
|
7
7
|
# @option options :prefix Prefix to put before the version (default = "html")
|
8
8
|
# @option options :zip The zip command
|
9
9
|
def call(release, options = {})
|
10
|
-
if options
|
11
|
-
options = @options.dup.update(options)
|
12
|
-
else
|
13
|
-
options = @options
|
14
|
-
end
|
15
|
-
|
16
10
|
options = {
|
17
|
-
:
|
18
|
-
:
|
19
|
-
}.update(options)
|
11
|
+
zip: "zip",
|
12
|
+
prefix: "html"
|
13
|
+
}.update(@options)
|
14
|
+
|
15
|
+
options.update(options) if options
|
20
16
|
|
21
17
|
name = [options[:prefix], release.scm.version].join("-") + ".zip"
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
begin
|
30
|
-
`#{options[:zip]} -v`
|
31
|
-
rescue Errno::ENOENT
|
32
|
-
raise RuntimeError, "Could not find zip in #{options[:zip].inspect}"
|
33
|
-
end
|
18
|
+
zip_path = release.target_path + name
|
19
|
+
|
20
|
+
release.log(self, "Finalizing release to #{zip_path}")
|
21
|
+
|
22
|
+
cleanup_existing_zip(zip_path)
|
23
|
+
|
24
|
+
check_zip_command(options[:zip])
|
34
25
|
|
35
26
|
::Dir.chdir(release.build_path) do
|
36
|
-
`#{options[:zip]} -r -9 "#{
|
27
|
+
`#{options[:zip]} -r -9 "#{zip_path}" ./*`
|
37
28
|
end
|
38
29
|
end
|
39
|
-
|
40
|
-
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def cleanup_existing_zip(path)
|
34
|
+
return unless File.exist?(path)
|
35
|
+
|
36
|
+
release.log(self, "Removing existing target #{path}")
|
37
|
+
FileUtils.rm_rf(path)
|
38
|
+
end
|
39
|
+
|
40
|
+
def check_zip_command(command)
|
41
|
+
`#{command} -v`
|
42
|
+
rescue Errno::ENOENT
|
43
|
+
raise "Could not find zip in #{command.inspect}"
|
44
|
+
end
|
41
45
|
end
|
42
46
|
end
|
43
|
-
|
44
|
-
Roger::Release::Finalizers.register(:zip, Roger::Release::Finalizers::Zip)
|
47
|
+
Roger::Release::Finalizers.register(:zip, Roger::Release::Finalizers::Zip)
|
@@ -1,49 +1,53 @@
|
|
1
|
-
require
|
1
|
+
require "tilt"
|
2
2
|
module Roger
|
3
|
-
|
3
|
+
# The Injector can inject variables and files into other files based on regexps.
|
4
|
+
#
|
4
5
|
# Inject VERSION / DATE (i.e. in TOC)
|
5
6
|
# r.inject({"VERSION" => release.version, "DATE" => release.date}, :into => %w{_doc/toc.html})
|
6
|
-
|
7
|
+
#
|
7
8
|
# Inject CHANGELOG
|
8
|
-
# r.inject({"CHANGELOG" => {:
|
9
|
-
|
9
|
+
# r.inject({"CHANGELOG" => {file: "", filter: BlueCloth}}, :into => %w{_doc/changelog.html})
|
10
10
|
class Release::Injector
|
11
|
-
|
12
11
|
# @example Simple variable injection (replaces [VARIABLE] into all .css files)
|
13
12
|
# {"[VARIABLE]" => "replacement"}, :into => %w{**/*.css}
|
14
13
|
#
|
15
14
|
# @example Regex variable injection (replaces all matches into test.js files)
|
16
|
-
# {/\/\*\s*\[BANNER\]\s*\*\// => "replacement"}, :into => %w{javacripts/test.js}
|
15
|
+
# {/\/\*\s*\[BANNER\]\s*\*\// => "replacement"}, :into => %w{javacripts/test.js}
|
17
16
|
#
|
18
|
-
# @example Simple variable injection with filtering (replaces [VARIABLE] with :content
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# @example Full file injection (replaces all matches of [CHANGELOG] with the contents of "CHANGELOG.md" into _doc/changelog.html)
|
17
|
+
# @example Simple variable injection with filtering (replaces [VARIABLE] with :content
|
18
|
+
# run through the markdown processor into all .html files)
|
22
19
|
#
|
23
|
-
# {"
|
20
|
+
# {"[VARIABLE]" => {content: "# header one", processor: "md"}, :into => %w{**/*.html}
|
24
21
|
#
|
25
|
-
# @example Full file injection
|
22
|
+
# @example Full file injection (replaces all matches of [CHANGELOG] with the contents
|
23
|
+
# of "CHANGELOG.md" into _doc/changelog.html)
|
26
24
|
#
|
27
|
-
# {"CHANGELOG" => {:
|
25
|
+
# {"CHANGELOG" => {file: "CHANGELOG.md"}}, :into => %w{_doc/changelog.html}
|
28
26
|
#
|
29
|
-
#
|
27
|
+
# @example Full file injection with filtering (replaces all matches of [CHANGELOG]
|
28
|
+
# with the contents of "CHANGELOG" which ran through Markdown compresser
|
29
|
+
# into _doc/changelog.html)
|
30
|
+
#
|
31
|
+
# {"CHANGELOG" => {file: "CHANGELOG", processor: "md"}}, :into => %w{_doc/changelog.html}
|
32
|
+
#
|
33
|
+
# Processors are based on Tilt (https://github.com/rtomayko/tilt).
|
30
34
|
# Currently supported/tested processors are:
|
31
35
|
#
|
32
36
|
# * 'md' for Markdown (bluecloth)
|
33
37
|
#
|
34
38
|
# Injection files are relative to the :source_path
|
35
|
-
#
|
39
|
+
#
|
36
40
|
# @param [Hash] variables Variables to inject. See example for more info
|
37
41
|
# @option options [Array] :into An array of file globs relative to the build_path
|
38
|
-
def initialize(variables, options)
|
42
|
+
def initialize(variables, options)
|
39
43
|
@variables = variables
|
40
44
|
@options = options
|
41
45
|
end
|
42
|
-
|
46
|
+
|
43
47
|
def call(release, options = {})
|
44
48
|
@options.update(options)
|
45
49
|
files = release.get_files(@options[:into])
|
46
|
-
|
50
|
+
|
47
51
|
files.each do |f|
|
48
52
|
c = File.read(f)
|
49
53
|
injected_vars = []
|
@@ -52,12 +56,13 @@ module Roger
|
|
52
56
|
injected_vars << variable
|
53
57
|
end
|
54
58
|
end
|
55
|
-
|
56
|
-
|
59
|
+
if injected_vars.size > 0
|
60
|
+
release.log(self, "Injected variables #{injected_vars.inspect} into #{f}")
|
61
|
+
end
|
62
|
+
File.open(f, "w") { |fh| fh.write c }
|
57
63
|
end
|
58
|
-
|
59
64
|
end
|
60
|
-
|
65
|
+
|
61
66
|
def get_content(injection, release)
|
62
67
|
case injection
|
63
68
|
when String
|
@@ -68,32 +73,33 @@ module Roger
|
|
68
73
|
if injection.respond_to?(:to_s)
|
69
74
|
injection.to_s
|
70
75
|
else
|
71
|
-
|
76
|
+
fail ArgumentError, "Woah, what's this? #{injection.inspect}"
|
72
77
|
end
|
73
78
|
end
|
74
79
|
end
|
75
|
-
|
80
|
+
|
76
81
|
def get_complex_injection(injection, release)
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
else
|
81
|
-
content = injection[:content]
|
82
|
-
end
|
83
|
-
|
84
|
-
raise ArgumentError, "No :content or :file specified" if !content
|
82
|
+
content = injection_content(injection, release)
|
83
|
+
|
84
|
+
fail ArgumentError, "No :content or :file specified" unless content
|
85
85
|
|
86
86
|
if injection[:processor]
|
87
87
|
if tmpl = Tilt[injection[:processor]]
|
88
|
-
(tmpl.new{ content }).render
|
88
|
+
(tmpl.new { content }).render
|
89
89
|
else
|
90
|
-
|
90
|
+
fail ArgumentError, "Unknown processor #{injection[:processor]}"
|
91
91
|
end
|
92
92
|
else
|
93
93
|
content
|
94
94
|
end
|
95
|
-
|
96
95
|
end
|
97
|
-
|
96
|
+
|
97
|
+
def injection_content(injection, release)
|
98
|
+
if injection[:file]
|
99
|
+
File.read(release.source_path + injection[:file])
|
100
|
+
else
|
101
|
+
injection[:content]
|
102
|
+
end
|
103
|
+
end
|
98
104
|
end
|
99
105
|
end
|
@@ -1,28 +1,30 @@
|
|
1
|
-
module Roger
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
raise ArgumentError, "Implement in subclass"
|
12
|
-
end
|
13
|
-
end
|
1
|
+
module Roger
|
2
|
+
class Release
|
3
|
+
# The Processors namespace
|
4
|
+
module Processors
|
5
|
+
# Abstract Processor class
|
6
|
+
class Base
|
7
|
+
def initialize(options = {})
|
8
|
+
@options = {}
|
9
|
+
@options.update(options) if options
|
10
|
+
end
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
12
|
+
def call(_release, _options = {})
|
13
|
+
fail ArgumentError, "Implement in subclass"
|
14
|
+
end
|
15
|
+
end
|
20
16
|
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
def self.register(name, processor)
|
18
|
+
fail ArgumentError, "Processor name '#{name.inspect}' already in use" if map.key?(name)
|
19
|
+
fail ArgumentError, "Name must be a symbol" unless name.is_a?(Symbol)
|
20
|
+
map[name] = processor
|
21
|
+
end
|
24
22
|
|
23
|
+
def self.map
|
24
|
+
@_map ||= {}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
25
28
|
end
|
26
|
-
|
27
29
|
require File.dirname(__FILE__) + "/processors/mockup"
|
28
30
|
require File.dirname(__FILE__) + "/processors/url_relativizer"
|
@@ -1,95 +1,123 @@
|
|
1
1
|
module Roger::Release::Processors
|
2
|
+
# The Mockup processor that will process all templates
|
2
3
|
class Mockup < Base
|
3
|
-
|
4
4
|
attr_accessor :project
|
5
|
-
|
6
|
-
|
5
|
+
|
6
|
+
MIME_TYPES_TO_EXTENSION = {
|
7
|
+
"text/html" => "html",
|
8
|
+
"text/css" => "css",
|
9
|
+
"application/javascript" => "js",
|
10
|
+
"text/xml" => "xml",
|
11
|
+
"application/xml" => "xml",
|
12
|
+
"text/csv" => "csv",
|
13
|
+
"application/json" => "json"
|
14
|
+
}
|
15
|
+
|
16
|
+
def initialize(options = {})
|
7
17
|
@options = {
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
18
|
+
env: {},
|
19
|
+
match: ["**/*.{html,md,html.erb}"],
|
20
|
+
skip: [/\Astylesheets/, /\Ajavascripts/]
|
11
21
|
}
|
12
|
-
|
13
|
-
@options.update(options) if options
|
22
|
+
|
23
|
+
@options.update(options) if options
|
14
24
|
end
|
15
25
|
|
16
|
-
def call(release, options={})
|
26
|
+
def call(release, options = {})
|
17
27
|
self.project = release.project
|
18
|
-
|
19
|
-
options =
|
20
|
-
|
21
|
-
options
|
22
|
-
|
23
|
-
release.log(self, "Processing mockup files")
|
24
|
-
|
25
|
-
release.log(self, " Matching: #{options[:match].inspect}", true)
|
26
|
-
release.log(self, " Skiping : #{options[:skip].inspect}", true)
|
27
|
-
release.log(self, " Env : #{options[:env].inspect}", true)
|
28
|
-
release.log(self, " Files :", true)
|
29
|
-
|
28
|
+
|
29
|
+
options = update_call_options(options)
|
30
|
+
|
31
|
+
log_call(options)
|
32
|
+
|
30
33
|
release.get_files(options[:match], options[:skip]).each do |file_path|
|
31
34
|
release.log(self, " Extract: #{file_path}", true)
|
32
35
|
self.run_on_file!(file_path, options[:env])
|
33
36
|
end
|
34
37
|
end
|
35
|
-
|
36
|
-
|
38
|
+
|
37
39
|
def run_on_file!(file_path, env = {})
|
38
|
-
template = Roger::Template.open(
|
39
|
-
|
40
|
+
template = Roger::Template.open(
|
41
|
+
file_path,
|
42
|
+
partials_path: project.partial_path,
|
43
|
+
layouts_path: project.layouts_path
|
44
|
+
)
|
45
|
+
|
40
46
|
# Clean up source file
|
41
47
|
FileUtils.rm(file_path)
|
42
|
-
|
48
|
+
|
43
49
|
# Write out new file
|
44
|
-
File.open(
|
50
|
+
File.open(target_path(file_path, template).to_s, "w") do |f|
|
51
|
+
f.write(template.render(env.dup))
|
52
|
+
end
|
45
53
|
end
|
46
|
-
|
54
|
+
|
47
55
|
# Runs the extractor on a single file and return processed source.
|
48
56
|
def extract_source_from_file(file_path, env = {})
|
49
|
-
Roger::Template.open(
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
57
|
+
Roger::Template.open(
|
58
|
+
file_path,
|
59
|
+
partials_path: project.partial_path,
|
60
|
+
layouts_path: project.layouts_path
|
61
|
+
).render(env.dup)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Determines the output path for a mockup path with a certain template
|
65
|
+
#
|
66
|
+
# @return [Pathname]
|
54
67
|
def target_path(path, template)
|
55
|
-
|
56
|
-
|
57
|
-
dir = Pathname.new(File.dirname(path.to_s))
|
58
|
-
|
59
|
-
# 2. Try to figure out the extension based on the template's mime-type
|
60
|
-
mime_types = {
|
61
|
-
"text/html" => "html",
|
62
|
-
"text/css" => "css",
|
63
|
-
"application/javascript" => "js",
|
64
|
-
"text/xml" => "xml",
|
65
|
-
"application/xml" => "xml",
|
66
|
-
"text/csv" => "csv",
|
67
|
-
"application/json" => "json"
|
68
|
-
}
|
69
|
-
extension = mime_types[template.template.class.default_mime_type]
|
70
|
-
|
68
|
+
parts, dir = split_path(path)
|
69
|
+
|
71
70
|
# Always return .html directly as it will cause too much trouble otherwise
|
72
|
-
if parts.last == "html"
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
71
|
+
return Pathname.new(path) if parts.last == "html"
|
72
|
+
|
73
|
+
# Strip last extension if we have a double extension
|
74
|
+
return dir + parts[0..-2].join(".") if parts.size > 2
|
75
|
+
|
76
|
+
dir + extension_based_on_mime_type(parts, template.template.class.default_mime_type)
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
def update_call_options(options)
|
82
|
+
{}.update(@options).update(options)
|
83
|
+
options[:env].update("roger.project" => project, "MOCKUP_PROJECT" => project)
|
84
|
+
options
|
85
|
+
end
|
86
|
+
|
87
|
+
def log_call(options)
|
88
|
+
release.log(self, "Processing mockup files")
|
89
|
+
|
90
|
+
release.log(self, " Matching: #{options[:match].inspect}", true)
|
91
|
+
release.log(self, " Skiping : #{options[:skip].inspect}", true)
|
92
|
+
release.log(self, " Env : #{options[:env].inspect}", true)
|
93
|
+
release.log(self, " Files :", true)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Split the path into two parts:
|
97
|
+
# 1. Filename, in an array, split by .
|
98
|
+
# 2. Pathname of directory
|
99
|
+
def split_path(path)
|
100
|
+
[
|
101
|
+
File.basename(path.to_s).split("."),
|
102
|
+
Pathname.new(File.dirname(path.to_s))
|
103
|
+
]
|
104
|
+
end
|
105
|
+
|
106
|
+
def extension_based_on_mime_type(parts, mime_type)
|
107
|
+
# 2. Try to figure out the extension based on the template's mime-type
|
108
|
+
extension = MIME_TYPES_TO_EXTENSION[mime_type]
|
109
|
+
|
110
|
+
# No matching extension, let's return path
|
111
|
+
return parts.join(".") if extension.nil?
|
112
|
+
|
113
|
+
if parts.size > 1
|
114
|
+
# Strip extension and replace with extension
|
115
|
+
(parts[0..-2] << extension).join(".")
|
79
116
|
else
|
80
|
-
|
81
|
-
|
82
|
-
if parts.size > 1
|
83
|
-
# Strip extension and replace with extension
|
84
|
-
dir + (parts[0..-2] << extension).join(".")
|
85
|
-
else
|
86
|
-
# Let's just add the extension
|
87
|
-
dir + (parts << extension).join(".")
|
88
|
-
end
|
117
|
+
# Let's just add the extension
|
118
|
+
(parts << extension).join(".")
|
89
119
|
end
|
90
|
-
end
|
91
|
-
|
120
|
+
end
|
92
121
|
end
|
93
122
|
end
|
94
|
-
|
95
|
-
Roger::Release::Processors.register(:mockup, Roger::Release::Processors::Mockup)
|
123
|
+
Roger::Release::Processors.register(:mockup, Roger::Release::Processors::Mockup)
|