roger 1.1.3 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +2 -0
  3. data/.rubocop.yml +47 -0
  4. data/.travis.yml +1 -5
  5. data/CHANGELOG.md +8 -0
  6. data/Gemfile +3 -3
  7. data/Rakefile +10 -4
  8. data/bin/roger +1 -1
  9. data/doc/mockupfile.md +97 -0
  10. data/doc/templating.md +5 -1
  11. data/examples/default_template/Gemfile +1 -1
  12. data/lib/roger/cli.rb +41 -36
  13. data/lib/roger/cli/command.rb +2 -4
  14. data/lib/roger/cli/generate.rb +1 -0
  15. data/lib/roger/cli/release.rb +2 -2
  16. data/lib/roger/cli/serve.rb +11 -11
  17. data/lib/roger/cli/test.rb +6 -5
  18. data/lib/roger/extractor.rb +42 -43
  19. data/lib/roger/generators.rb +27 -19
  20. data/lib/roger/generators/generator.rb +7 -10
  21. data/lib/roger/generators/new.rb +56 -41
  22. data/lib/roger/generators/templates/generator.tt +5 -5
  23. data/lib/roger/helpers/get_callable.rb +15 -14
  24. data/lib/roger/helpers/logging.rb +35 -13
  25. data/lib/roger/mockupfile.rb +13 -23
  26. data/lib/roger/project.rb +41 -34
  27. data/lib/roger/rack/roger.rb +28 -29
  28. data/lib/roger/rack/sleep.rb +4 -5
  29. data/lib/roger/release.rb +95 -72
  30. data/lib/roger/release/cleaner.rb +14 -13
  31. data/lib/roger/release/finalizers.rb +10 -10
  32. data/lib/roger/release/finalizers/dir.rb +17 -19
  33. data/lib/roger/release/finalizers/git_branch.rb +76 -38
  34. data/lib/roger/release/finalizers/rsync.rb +60 -49
  35. data/lib/roger/release/finalizers/zip.rb +32 -29
  36. data/lib/roger/release/injector.rb +43 -37
  37. data/lib/roger/release/processors.rb +24 -22
  38. data/lib/roger/release/processors/mockup.rb +97 -69
  39. data/lib/roger/release/processors/url_relativizer.rb +57 -30
  40. data/lib/roger/release/scm.rb +30 -27
  41. data/lib/roger/release/scm/git.rb +101 -92
  42. data/lib/roger/resolver.rb +86 -61
  43. data/lib/roger/server.rb +52 -27
  44. data/lib/roger/template.rb +102 -74
  45. data/lib/roger/test.rb +16 -13
  46. data/lib/roger/version.rb +3 -2
  47. data/roger.gemspec +9 -5
  48. data/test/helpers/cli.rb +17 -15
  49. data/test/project/Gemfile +2 -2
  50. data/test/project/html/formats/csv.rcsv +0 -0
  51. data/test/project/lib/generators/test.rb +2 -3
  52. data/test/project/lib/tests/fail/fail.rb +5 -6
  53. data/test/project/lib/tests/noop/lib/cli.rb +2 -1
  54. data/test/project/lib/tests/noop/lib/test.rb +5 -5
  55. data/test/project/lib/tests/noop/noop.rb +2 -1
  56. data/test/project/lib/tests/succeed/succeed.rb +5 -6
  57. data/test/unit/cli/cli_base_test.rb +2 -3
  58. data/test/unit/cli/cli_generate_test.rb +9 -10
  59. data/test/unit/cli/cli_serve_test.rb +22 -18
  60. data/test/unit/cli/cli_test_test.rb +13 -15
  61. data/test/unit/cli/cli_version_test.rb +4 -4
  62. data/test/unit/generators_test.rb +8 -10
  63. data/test/unit/helpers/logging_test.rb +64 -0
  64. data/test/unit/rack/roger_test.rb +21 -0
  65. data/test/unit/release/cleaner_test.rb +23 -19
  66. data/test/unit/release/finalizers/git_branch_test.rb +2 -1
  67. data/test/unit/release/finalizers/zip_test.rb +48 -0
  68. data/test/unit/release/mockup_test.rb +48 -0
  69. data/test/unit/release/processors_test.rb +19 -19
  70. data/test/unit/release_test.rb +15 -14
  71. data/test/unit/resolver_test.rb +21 -14
  72. data/test/unit/server_test.rb +31 -0
  73. data/test/unit/template_test.rb +58 -36
  74. data/test/unit/test_test.rb +3 -2
  75. metadata +35 -9
  76. 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
- :zip => "zip",
18
- :prefix => "html"
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
- release.log(self, "Finalizing release to #{release.target_path + name}")
23
-
24
- if File.exist?(release.target_path + name)
25
- release.log(self, "Removing existing target #{release.target_path + name}")
26
- FileUtils.rm_rf(release.target_path + name)
27
- end
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 "#{release.target_path + name}" ./*`
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 'tilt'
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" => {:file => "", :filter => BlueCloth}}, :into => %w{_doc/changelog.html})
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 run through the markdown processor into all .html files)
19
- # {"[VARIABLE]" => {:content => "# header one", :processor => "md"}, :into => %w{**/*.html}
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
- # {"CHANGELOG" => {:file => "CHANGELOG.md"}}, :into => %w{_doc/changelog.html}
20
+ # {"[VARIABLE]" => {content: "# header one", processor: "md"}, :into => %w{**/*.html}
24
21
  #
25
- # @example Full file injection with filtering (replaces all matches of [CHANGELOG] with the contents of "CHANGELOG" which ran through Markdown compresser into _doc/changelog.html)
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" => {:file => "CHANGELOG", :processor => "md"}}, :into => %w{_doc/changelog.html}
25
+ # {"CHANGELOG" => {file: "CHANGELOG.md"}}, :into => %w{_doc/changelog.html}
28
26
  #
29
- # Processors are based on Tilt (https://github.com/rtomayko/tilt).
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
- release.log(self, "Injected variables #{injected_vars.inspect} into #{f}") if injected_vars.size > 0
56
- File.open(f,"w") { |fh| fh.write c }
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
- raise ArgumentError, "Woah, what's this? #{injection.inspect}"
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
- if injection[:file]
79
- content = File.read(release.source_path + injection[:file])
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
- raise ArgumentError, "Unknown processor #{injection[:processor]}"
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::Release::Processors
2
- class Base
3
-
4
- def initialize(options = {})
5
- @options = {}
6
- @options.update(options) if options
7
- end
8
-
9
-
10
- def call(release, options = {})
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
- def self.register(name, processor)
16
- raise ArgumentError, "Another processor has already claimed the name #{name.inspect}" if self.map.has_key?(name)
17
- raise ArgumentError, "Name must be a symbol" unless name.kind_of?(Symbol)
18
- self.map[name] = processor
19
- end
12
+ def call(_release, _options = {})
13
+ fail ArgumentError, "Implement in subclass"
14
+ end
15
+ end
20
16
 
21
- def self.map
22
- @_map ||= {}
23
- end
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
- def initialize(options={})
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
- :env => {},
9
- :match => ["**/*.{html,md,html.erb}"],
10
- :skip => [/\Astylesheets/, /\Ajavascripts/]
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 = {}.update(@options).update(options)
20
-
21
- options[:env].update("MOCKUP_PROJECT" => project)
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(file_path, :partials_path => self.project.partial_path, :layouts_path => self.project.layouts_path)
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(self.target_path(file_path, template),"w"){|f| f.write(template.render(env.dup)) }
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(file_path, :partials_path => self.project.partial_path, :layouts_path => self.project.layouts_path).render(env.dup)
50
- end
51
-
52
- protected
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
- # 1. If we have a double extension we rip of the template it's own extension and be done with it
56
- parts = File.basename(path.to_s).split(".")
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
- return path
74
- end
75
-
76
- if parts.size > 2
77
- # Strip extension
78
- dir + parts[0..-2].join(".")
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
- return path if extension.nil?
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)