roger 1.1.3 → 1.2.1

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.
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)