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,47 +1,74 @@
1
- require File.dirname(__FILE__) + '../../../resolver'
1
+ require File.dirname(__FILE__) + "../../../resolver"
2
2
 
3
3
  module Roger::Release::Processors
4
+ # URL relativizer processor
5
+ # The relativizer can be used to rewrite absolute paths in attributes to relative paths
6
+ # during release.
4
7
  class UrlRelativizer < Base
5
-
6
- def initialize(options={})
8
+ def initialize(options = {})
7
9
  @options = {
8
- :url_attributes => %w{src href action},
9
- :match => ["**/*.html"],
10
- :skip => []
10
+ url_attributes: %w(src href action),
11
+ match: ["**/*.html"],
12
+ skip: []
11
13
  }
12
-
13
- @options.update(options) if options
14
+
15
+ @options.update(options) if options
14
16
  end
15
17
 
16
- def call(release, options={})
18
+ def call(release, options = {})
17
19
  options = {}.update(@options).update(options)
18
-
19
- release.log(self, "Relativizing all URLS in #{options[:match].inspect} files in attributes #{options[:url_attributes].inspect}, skipping #{options[:skip].any? ? options[:skip].inspect : "none" }")
20
-
20
+
21
+ log_call(options)
22
+
21
23
  @resolver = Roger::Resolver.new(release.build_path)
22
- release.get_files(options[:match], options[:skip]).each do |file_path|
24
+
25
+ relativize_attributes_in_files(
26
+ options[:url_attributes],
27
+ release.get_files(options[:match], options[:skip])
28
+ )
29
+ end
30
+
31
+ protected
32
+
33
+ def log_call(options)
34
+ log_message = "Relativizing all URLS in #{options[:match].inspect}"
35
+ log_message << "files in attributes #{options[:url_attributes].inspect},"
36
+ log_message << "skipping #{options[:skip].any? ? options[:skip].inspect : 'none' }"
37
+ release.log(self, log_message)
38
+ end
39
+
40
+ def relativize_attributes_in_files(attributes, files)
41
+ files.each do |file_path|
23
42
  release.debug(self, "Relativizing URLS in #{file_path}") do
24
- orig_source = File.read(file_path)
25
- File.open(file_path,"w") do |f|
26
- doc = Hpricot(orig_source)
27
- options[:url_attributes].each do |attribute|
28
- (doc/"*[@#{attribute}]").each do |tag|
29
- converted_url = @resolver.url_to_relative_url(tag[attribute], file_path)
30
- release.debug(self, "Converting '#{tag[attribute]}' to '#{converted_url}'")
31
- case converted_url
32
- when String
33
- tag[attribute] = converted_url
34
- when nil
35
- release.log(self, "Could not resolve link #{tag[attribute]} in #{file_path}")
36
- end
37
- end
38
- end
39
- f.write(doc.to_original_html)
43
+ relativize_attributes_in_file(attributes, file_path)
44
+ end
45
+ end
46
+ end
47
+
48
+ def relativize_attributes_in_file(attributes, file_path)
49
+ orig_source = File.read(file_path)
50
+ File.open(file_path, "w") do |f|
51
+ f.write(relativize_attributes_in_source(attributes, orig_source, file_path))
52
+ end
53
+ end
54
+
55
+ def relativize_attributes_in_source(attributes, source, file_path)
56
+ doc = Hpricot(source)
57
+ attributes.each do |attribute|
58
+ (doc / "*[@#{attribute}]").each do |tag|
59
+ converted_url = @resolver.url_to_relative_url(tag[attribute], file_path)
60
+ release.debug(self, "Converting '#{tag[attribute]}' to '#{converted_url}'")
61
+ case converted_url
62
+ when String
63
+ tag[attribute] = converted_url
64
+ when nil
65
+ release.log(self, "Could not resolve link #{tag[attribute]} in #{file_path}")
40
66
  end
41
67
  end
42
68
  end
69
+ doc.to_original_html
43
70
  end
44
71
  end
45
72
  end
46
73
 
47
- Roger::Release::Processors.register(:url_relativizer, Roger::Release::Processors::UrlRelativizer)
74
+ Roger::Release::Processors.register(:url_relativizer, Roger::Release::Processors::UrlRelativizer)
@@ -1,31 +1,34 @@
1
- module Roger::Release::Scm
2
- class Base
3
-
4
- attr_reader :config
5
-
6
- def initialize(config={})
7
- @config = config
8
- end
9
-
10
- # Returns the release version string from the SCM
11
- #
12
- # @return String The current version string
13
- def version
14
- raise "Implement in subclass"
15
- end
16
-
17
- # Returns the release version date from the SCM
18
- def date
19
- raise "Implement in subclass"
20
- end
21
-
22
- # Returns a Release::Scm object with the previous version's data
23
- #
24
- # @return Roger::Release::Scm The previous version
25
- def previous
26
- raise "Implement in subclass"
1
+ module Roger
2
+ class Release
3
+ module Scm
4
+ # Abstract SCM base class
5
+ class Base
6
+ attr_reader :config
7
+
8
+ def initialize(config = {})
9
+ @config = config
10
+ end
11
+
12
+ # Returns the release version string from the SCM
13
+ #
14
+ # @return String The current version string
15
+ def version
16
+ fail "Implement in subclass"
17
+ end
18
+
19
+ # Returns the release version date from the SCM
20
+ def date
21
+ fail "Implement in subclass"
22
+ end
23
+
24
+ # Returns a Release::Scm object with the previous version's data
25
+ #
26
+ # @return Roger::Release::Scm The previous version
27
+ def previous
28
+ fail "Implement in subclass"
29
+ end
30
+ end
27
31
  end
28
-
29
32
  end
30
33
  end
31
34
 
@@ -1,106 +1,115 @@
1
- require 'pathname'
2
-
3
- module Roger::Release::Scm
4
- class Git < Base
5
-
6
- # @option config [String] :ref Ref to use for current tag
7
- # @option config [String, Pathname] :path Path to working dir
8
- def initialize(config={})
9
- super(config)
10
- @config[:ref] ||= "HEAD"
11
- end
12
-
13
- # Version is either:
14
- # - the tagged version number (first "v" will be stripped) or
15
- # - the return value of "git describe --tags HEAD"
16
- # - the short SHA1 if there hasn't been a previous tag
17
- def version
18
- get_scm_data if @_version.nil?
19
- @_version
20
- end
21
-
22
- # Date will be Time.now if it can't be determined from GIT repository
23
- def date
24
- get_scm_data if @_date.nil?
25
- @_date
26
- end
27
-
28
- def previous
29
- self.class.new(@config.dup.update(:ref => get_previous_tag_name))
30
- end
31
-
32
- protected
33
-
34
- def get_previous_tag_name
35
- # Get list of SHA1 that have a ref
36
- begin
37
- sha1s = `git --git-dir=#{safe_git_dir} log --pretty='%H' --simplify-by-decoration`.split("\n")
38
- tags = []
39
- while tags.size < 2 && sha1s.any?
40
- sha1 = sha1s.shift
41
- tag = `git --git-dir=#{safe_git_dir} describe --tags --exact-match #{sha1} 2>/dev/null`.strip
42
- tags << tag if !tag.empty?
1
+ require "pathname"
2
+
3
+ module Roger
4
+ class Release
5
+ module Scm
6
+ # The GIT SCM implementation for Roger release
7
+ class Git < Base
8
+ # @option config [String] :ref Ref to use for current tag
9
+ # @option config [String, Pathname] :path Path to working dir
10
+ def initialize(config = {})
11
+ super(config)
12
+ @config[:ref] ||= "HEAD"
43
13
  end
44
- tags.last
45
- rescue
46
- raise "Could not get previous tag"
47
- end
48
- end
49
-
50
- def git_dir
51
- @git_dir ||= find_git_dir(@config[:path])
52
- end
53
14
 
54
- # Safely escaped git dir
55
- def safe_git_dir
56
- Shellwords.escape(self.git_dir.to_s)
57
- end
15
+ # Version is either:
16
+ # - the tagged version number (first "v" will be stripped) or
17
+ # - the return value of "git describe --tags HEAD"
18
+ # - the short SHA1 if there hasn't been a previous tag
19
+ def version
20
+ get_scm_data if @_version.nil?
21
+ @_version
22
+ end
23
+
24
+ # Date will be Time.now if it can't be determined from GIT repository
25
+ def date
26
+ get_scm_data if @_date.nil?
27
+ @_date
28
+ end
29
+
30
+ def previous
31
+ self.class.new(@config.dup.update(ref: get_previous_tag_name))
32
+ end
33
+
34
+ protected
35
+
36
+ def get_previous_tag_name
37
+ # Get list of SHA1 that have a ref
38
+ sha1s = `git --git-dir=#{safe_git_dir} log --pretty='%H' --simplify-by-decoration`
39
+ sha1s = sha1s.split("\n")
40
+ tags = []
41
+ while tags.size < 2 && sha1s.any?
42
+ sha1 = sha1s.shift
43
+ tag = `git --git-dir=#{safe_git_dir} describe --tags --exact-match #{sha1} 2>/dev/null`
44
+ tag = tag.strip
45
+ tags << tag unless tag.empty?
46
+ end
47
+ tags.last
48
+ rescue
49
+ raise "Could not get previous tag"
50
+ end
51
+
52
+ def git_dir
53
+ @git_dir ||= find_git_dir(@config[:path])
54
+ end
55
+
56
+ # Safely escaped git dir
57
+ def safe_git_dir
58
+ Shellwords.escape(git_dir.to_s)
59
+ end
60
+
61
+ # Preload version control data.
62
+ def get_scm_data(ref = @config[:ref])
63
+ @_version = scm_version(ref) || ""
64
+ @_date = scm_date(ref) || Time.now
65
+ end
66
+
67
+ # Some hackery to determine if ref is on a tagged version or not
68
+ # @return [String, nil] Will return version number if available, nil otherwise
69
+ def scm_version(ref)
70
+ return nil unless File.exist?(git_dir)
71
+
72
+ version = `git --git-dir=#{safe_git_dir} describe --tags #{ref} 2>&1`
58
73
 
59
- # Some hackery to determine if we're on a tagged version or not
60
- def get_scm_data(ref = @config[:ref])
61
- @_version = ""
62
- @_date = Time.now
63
- begin
64
- if File.exist?(git_dir)
65
- @_version = `git --git-dir=#{safe_git_dir} describe --tags #{ref} 2>&1`
66
-
67
- if $?.to_i > 0
68
- # HEAD is not a tagged verison, get the short SHA1 instead
69
- @_version = `git --git-dir=#{safe_git_dir} show #{ref} --format=format:"%h" -s 2>&1`
74
+ if $CHILD_STATUS.to_i > 0
75
+ # HEAD is not a tagged version, get the short SHA1 instead
76
+ version = `git --git-dir=#{safe_git_dir} show #{ref} --format=format:"%h" -s 2>&1`
70
77
  else
71
78
  # HEAD is a tagged version, if version is prefixed with "v" it will be stripped off
72
- @_version.gsub!(/^v/,"")
79
+ version.gsub!(/^v/, "")
73
80
  end
74
- @_version.strip!
75
-
81
+
82
+ version.strip!
83
+ rescue RuntimeError
84
+ nil
85
+ end
86
+
87
+ # Get date of ref from git
88
+ # @return [Time, nil] Returns time if available and parseable, nil otherwise
89
+ def scm_date(ref)
90
+ return nil unless File.exist?(git_dir)
91
+
76
92
  # Get the date in epoch time
77
93
  date = `git --git-dir=#{safe_git_dir} show #{ref} --format=format:"%ct" -s 2>&1`
78
- if date =~ /\d+/
79
- @_date = Time.at(date.to_i)
80
- else
81
- @_date = Time.now
82
- end
83
-
94
+ Time.at(date.to_i) if date =~ /\d+/
95
+ rescue RuntimeError
96
+ nil
84
97
  end
85
- rescue RuntimeError => e
86
- end
87
98
 
88
- end
89
-
90
- # Find the git dir
91
- def find_git_dir(path)
92
- path = Pathname.new(path).realpath
93
- while path.parent != path && !(path + ".git").directory?
94
- path = path.parent
95
- end
96
-
97
- path = path + ".git"
98
-
99
- raise "Could not find suitable .git dir in #{path}" if !path.directory?
99
+ # Find the git dir
100
+ def find_git_dir(path)
101
+ path = Pathname.new(path).realpath
102
+ while path.parent != path && !(path + ".git").directory?
103
+ path = path.parent
104
+ end
100
105
 
101
- path
106
+ path += ".git"
107
+
108
+ fail "Could not find suitable .git dir in #{path}" unless path.directory?
109
+
110
+ path
111
+ end
112
+ end
102
113
  end
103
-
104
114
  end
105
115
  end
106
-
@@ -1,92 +1,72 @@
1
1
  module Roger
2
+ # The resolver is here to resolve urls to paths and sometimes vice-versa
2
3
  class Resolver
3
-
4
4
  attr_reader :load_paths
5
-
5
+
6
6
  def initialize(paths)
7
- raise ArgumentError, "Resolver base path can't be nil" if paths.nil?
7
+ fail ArgumentError, "Resolver base path can't be nil" if paths.nil?
8
8
 
9
9
  # Convert to paths
10
- @load_paths = [paths].flatten.map{|p| Pathname.new(p) }
10
+ @load_paths = [paths].flatten.map { |p| Pathname.new(p) }
11
11
  end
12
-
12
+
13
13
  # @param [String] url The url to resolve to a path
14
14
  # @param [Hash] options Options
15
15
  #
16
- # @option options [true,false] :exact_match Wether or not to match exact paths, this is mainly used in the path_to_url method to match .js, .css, etc files.
17
- # @option options [String] :preferred_extension The part to chop off and re-add to search for more complex double-extensions. (Makes it possible to have context aware partials)
16
+ # @option options [true,false] :exact_match Wether or not to match exact paths,
17
+ # this is mainly used in the path_to_url method to match .js, .css, etc files.
18
+ # @option options [String] :preferred_extension The part to chop off
19
+ # and re-add to search for more complex double-extensions. (Makes it possible to have context
20
+ # aware partials)
18
21
  def find_template(url, options = {})
19
- orig_path, qs, anch = strip_query_string_and_anchor(url.to_s)
20
-
21
22
  options = {
22
- :exact_match => false,
23
- :preferred_extension => "html"
23
+ exact_match: false,
24
+ preferred_extension: "html"
24
25
  }.update(options)
25
26
 
26
- paths = self.load_paths.map{|base| File.join(base, orig_path) }
27
-
28
- paths.find do |path|
29
- if options[:exact_match] && File.exist?(path)
30
- return Pathname.new(path)
31
- end
32
-
33
- # It's a directory, add "/index"
34
- if File.directory?(path)
35
- path = File.join(path, "index")
36
- end
37
-
38
- # 2. If it's preferred_extension, we strip of the extension
39
- if path =~ /\.#{options[:preferred_extension]}\Z/
40
- path.sub!(/\.#{options[:preferred_extension]}\Z/, "")
41
- end
42
-
43
- extensions = Tilt.default_mapping.template_map.keys + Tilt.default_mapping.lazy_map.keys
27
+ orig_path, _qs, _anch = strip_query_string_and_anchor(url.to_s)
44
28
 
45
- # We have to re-add preferred_extension again as we have stripped it in in step 2!
46
- extensions += extensions.map{|ext| "#{options[:preferred_extension]}.#{ext}"}
29
+ output = nil
47
30
 
48
- if found_extension = extensions.find { |ext| File.exist?(path + "." + ext) }
49
- return Pathname.new(path + "." + found_extension)
50
- end
31
+ load_paths.find do |load_path|
32
+ path = File.join(load_path, orig_path)
51
33
 
52
- false #Next iteration
34
+ # If it's an exact match we're done
35
+ if options[:exact_match] && File.exist?(path)
36
+ output = Pathname.new(path)
37
+ else
38
+ output = find_file_with_extension(path, options[:preferred_extension])
39
+ end
53
40
  end
41
+
42
+ output
54
43
  end
55
- alias :url_to_path :find_template
56
-
57
-
44
+ alias_method :url_to_path, :find_template
45
+
58
46
  # Convert a disk path on file to an url
59
47
  def path_to_url(path, relative_to = nil)
60
-
61
48
  # Find the parent path we're in
62
49
  path = Pathname.new(path).realpath
63
- base = self.load_paths.find{|lp| path.to_s =~ /\A#{Regexp.escape(lp.realpath.to_s)}/ }
50
+ base = load_paths.find { |lp| path.to_s =~ /\A#{Regexp.escape(lp.realpath.to_s)}/ }
64
51
 
65
52
  path = path.relative_path_from(base).cleanpath
66
-
53
+
67
54
  if relative_to
68
- if relative_to.to_s =~ /\A\//
69
- relative_to = Pathname.new(File.dirname(relative_to.to_s)).relative_path_from(base).cleanpath
70
- else
71
- relative_to = Pathname.new(File.dirname(relative_to.to_s))
72
- end
73
- path = Pathname.new("/" + path.to_s).relative_path_from(Pathname.new("/" + relative_to.to_s))
74
- path.to_s
55
+ relative_path_to_url(path, relative_to, base).to_s
75
56
  else
76
- "/" + path.to_s
57
+ "/#{path}"
77
58
  end
78
-
79
59
  end
80
-
60
+
81
61
  def url_to_relative_url(url, relative_to_path)
82
62
  # Skip if the url doesn't start with a / (but not with //)
83
- return false unless url =~ /\A\/[^\/]/
84
-
63
+ return false unless url =~ %r{\A/[^/]}
64
+
85
65
  path, qs, anch = strip_query_string_and_anchor(url)
86
66
 
87
67
  # Get disk path
88
- if true_path = self.url_to_path(path, :exact_match => true)
89
- path = self.path_to_url(true_path, relative_to_path)
68
+ if true_path = url_to_path(path, exact_match: true)
69
+ path = path_to_url(true_path, relative_to_path)
90
70
  path += qs if qs
91
71
  path += anch if anch
92
72
  path
@@ -94,26 +74,71 @@ module Roger
94
74
  false
95
75
  end
96
76
  end
97
-
77
+
98
78
  def strip_query_string_and_anchor(url)
99
79
  url = url.dup
100
-
80
+
101
81
  # Strip off anchors
102
82
  anchor = nil
103
83
  url.gsub!(/(#.+)\Z/) do |r|
104
84
  anchor = r
105
85
  ""
106
86
  end
107
-
87
+
108
88
  # Strip off query strings
109
89
  query = nil
110
90
  url.gsub!(/(\?.+)\Z/) do |r|
111
91
  query = r
112
92
  ""
113
93
  end
114
-
94
+
115
95
  [url, query, anchor]
116
- end
117
-
96
+ end
97
+
98
+ protected
99
+
100
+ # Tries all extensions on path to see what file exists
101
+ # @return [Pathname,nil] returns a pathname of the full file path if found. nil otherwise
102
+ def find_file_with_extension(path, preferred_extension)
103
+ output = nil
104
+
105
+ file_path = path
106
+
107
+ # If it's a directory, add "/index"
108
+ file_path = File.join(file_path, "index") if File.directory?(file_path)
109
+
110
+ # Strip of extension
111
+ if path =~ /\.#{preferred_extension}\Z/
112
+ file_path.sub!(/\.#{preferred_extension}\Z/, "")
113
+ end
114
+
115
+ possible_extensions(preferred_extension).find do |ext|
116
+ path_with_extension = file_path + "." + ext
117
+ if File.exist?(path_with_extension)
118
+ output = Pathname.new(path_with_extension)
119
+ end
120
+ end
121
+ output
122
+ end
123
+
124
+ # Makes a list of all Tilt extensions
125
+ # Also adds a list of double extensions. Example:
126
+ # tilt_extensions = %w(erb md); second_extension = "html"
127
+ # return %w(erb md html.erb html.md)
128
+ def possible_extensions(second_extension)
129
+ extensions = Tilt.default_mapping.template_map.keys + Tilt.default_mapping.lazy_map.keys
130
+ extensions + extensions.map { |ext| "#{second_extension}.#{ext}" }
131
+ end
132
+
133
+ def relative_path_to_url(path, relative_to, base)
134
+ relative_to = Pathname.new(File.dirname(relative_to.to_s))
135
+
136
+ # If relative_to is an absolute path
137
+ if relative_to.to_s =~ %r{\A/}
138
+ relative_to = relative_to.relative_path_from(base).cleanpath
139
+ end
140
+
141
+ Pathname.new("/" + path.to_s).relative_path_from(Pathname.new("/" + relative_to.to_s))
142
+ end
118
143
  end
119
144
  end