amber 0.2.6 → 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +14 -1
  3. data/README.md +88 -19
  4. data/bin/amber +26 -13
  5. data/lib/amber.rb +25 -39
  6. data/lib/amber/cli.rb +25 -1
  7. data/lib/amber/i18n.rb +43 -0
  8. data/lib/amber/render/apache.rb +44 -0
  9. data/lib/amber/render/asset.rb +28 -2
  10. data/lib/amber/render/helpers/blog_helper.rb +35 -0
  11. data/lib/amber/render/helpers/date_helper.rb +14 -0
  12. data/lib/amber/render/helpers/haml_helper.rb +9 -0
  13. data/lib/amber/render/helpers/html_helper.rb +30 -8
  14. data/lib/amber/render/helpers/navigation_helper.rb +31 -32
  15. data/lib/amber/render/layout.rb +2 -1
  16. data/lib/amber/render/template.rb +24 -11
  17. data/lib/amber/render/view.rb +31 -12
  18. data/lib/amber/server.rb +122 -56
  19. data/lib/amber/site.rb +171 -29
  20. data/lib/amber/site_configuration.rb +26 -11
  21. data/lib/amber/static_page.rb +34 -20
  22. data/lib/amber/static_page/filesystem.rb +3 -7
  23. data/lib/amber/static_page/page_properties.rb +7 -0
  24. data/lib/amber/static_page/render.rb +47 -31
  25. data/lib/amber/templates/apache_config.erb +12 -0
  26. data/lib/amber/templates/apache_config_with_prefix.erb +14 -0
  27. data/lib/amber/templates/htaccess.erb +176 -0
  28. data/lib/amber/version.rb +5 -0
  29. data/locales/rails-i18n/af.yml +201 -0
  30. data/locales/rails-i18n/ar.yml +274 -0
  31. data/locales/rails-i18n/az.yml +192 -0
  32. data/locales/rails-i18n/bg.yml +192 -0
  33. data/locales/rails-i18n/bn.yml +175 -0
  34. data/locales/rails-i18n/bs.yml +243 -0
  35. data/locales/rails-i18n/ca.yml +192 -0
  36. data/locales/rails-i18n/cs.yml +210 -0
  37. data/locales/rails-i18n/cy.yml +242 -0
  38. data/locales/rails-i18n/da.yml +198 -0
  39. data/locales/rails-i18n/de-AT.yml +202 -0
  40. data/locales/rails-i18n/de-CH.yml +206 -0
  41. data/locales/rails-i18n/de.yml +202 -0
  42. data/locales/rails-i18n/el.yml +198 -0
  43. data/locales/rails-i18n/en-AU.yml +198 -0
  44. data/locales/rails-i18n/en-CA.yml +207 -0
  45. data/locales/rails-i18n/en-GB.yml +198 -0
  46. data/locales/rails-i18n/en-IE.yml +198 -0
  47. data/locales/rails-i18n/en-IN.yml +198 -0
  48. data/locales/rails-i18n/en-NZ.yml +198 -0
  49. data/locales/rails-i18n/en-US.yml +204 -0
  50. data/locales/rails-i18n/en-ZA.yml +204 -0
  51. data/locales/rails-i18n/en.yml +204 -0
  52. data/locales/rails-i18n/eo.yml +194 -0
  53. data/locales/rails-i18n/es-419.yml +207 -0
  54. data/locales/rails-i18n/es-AR.yml +198 -0
  55. data/locales/rails-i18n/es-CL.yml +192 -0
  56. data/locales/rails-i18n/es-CO.yml +198 -0
  57. data/locales/rails-i18n/es-CR.yml +198 -0
  58. data/locales/rails-i18n/es-EC.yml +204 -0
  59. data/locales/rails-i18n/es-MX.yml +204 -0
  60. data/locales/rails-i18n/es-PA.yml +204 -0
  61. data/locales/rails-i18n/es-PE.yml +177 -0
  62. data/locales/rails-i18n/es-US.yml +198 -0
  63. data/locales/rails-i18n/es-VE.yml +198 -0
  64. data/locales/rails-i18n/es.yml +192 -0
  65. data/locales/rails-i18n/et.yml +192 -0
  66. data/locales/rails-i18n/eu.yml +192 -0
  67. data/locales/rails-i18n/fa.yml +192 -0
  68. data/locales/rails-i18n/fi.yml +192 -0
  69. data/locales/rails-i18n/fr-CA.yml +200 -0
  70. data/locales/rails-i18n/fr-CH.yml +200 -0
  71. data/locales/rails-i18n/fr.yml +214 -0
  72. data/locales/rails-i18n/gl.yml +171 -0
  73. data/locales/rails-i18n/he.yml +194 -0
  74. data/locales/rails-i18n/hi-IN.yml +192 -0
  75. data/locales/rails-i18n/hi.yml +192 -0
  76. data/locales/rails-i18n/hr.yml +236 -0
  77. data/locales/rails-i18n/hu.yml +192 -0
  78. data/locales/rails-i18n/id.yml +193 -0
  79. data/locales/rails-i18n/is.yml +212 -0
  80. data/locales/rails-i18n/iso-639-2/csb.yml +203 -0
  81. data/locales/rails-i18n/iso-639-2/dsb.yml +208 -0
  82. data/locales/rails-i18n/iso-639-2/fur.yml +192 -0
  83. data/locales/rails-i18n/iso-639-2/gsw-CH.yml +196 -0
  84. data/locales/rails-i18n/iso-639-2/hsb.yml +207 -0
  85. data/locales/rails-i18n/iso-639-2/pap-AW.yml +205 -0
  86. data/locales/rails-i18n/iso-639-2/pap-CW.yml +205 -0
  87. data/locales/rails-i18n/iso-639-2/scr.yml +236 -0
  88. data/locales/rails-i18n/it-CH.yml +198 -0
  89. data/locales/rails-i18n/it.yml +204 -0
  90. data/locales/rails-i18n/ja.yml +194 -0
  91. data/locales/rails-i18n/km.yml +54 -0
  92. data/locales/rails-i18n/kn.yml +192 -0
  93. data/locales/rails-i18n/ko.yml +190 -0
  94. data/locales/rails-i18n/lo.yml +227 -0
  95. data/locales/rails-i18n/lt.yml +217 -0
  96. data/locales/rails-i18n/lv.yml +229 -0
  97. data/locales/rails-i18n/mk.yml +198 -0
  98. data/locales/rails-i18n/mn.yml +198 -0
  99. data/locales/rails-i18n/ms.yml +200 -0
  100. data/locales/rails-i18n/nb.yml +205 -0
  101. data/locales/rails-i18n/ne.yml +198 -0
  102. data/locales/rails-i18n/nl.yml +204 -0
  103. data/locales/rails-i18n/nn.yml +201 -0
  104. data/locales/rails-i18n/or.yml +192 -0
  105. data/locales/rails-i18n/pl.yml +236 -0
  106. data/locales/rails-i18n/pt-BR.yml +206 -0
  107. data/locales/rails-i18n/pt.yml +200 -0
  108. data/locales/rails-i18n/rm.yml +223 -0
  109. data/locales/rails-i18n/ro.yml +205 -0
  110. data/locales/rails-i18n/ru.yml +256 -0
  111. data/locales/rails-i18n/sk.yml +206 -0
  112. data/locales/rails-i18n/sl.yml +205 -0
  113. data/locales/rails-i18n/sr.yml +236 -0
  114. data/locales/rails-i18n/sv.yml +192 -0
  115. data/locales/rails-i18n/sw.yml +190 -0
  116. data/locales/rails-i18n/ta.yml +204 -0
  117. data/locales/rails-i18n/th.yml +166 -0
  118. data/locales/rails-i18n/tl.yml +224 -0
  119. data/locales/rails-i18n/tr.yml +204 -0
  120. data/locales/rails-i18n/uk.yml +250 -0
  121. data/locales/rails-i18n/ur.yml +204 -0
  122. data/locales/rails-i18n/uz.yml +250 -0
  123. data/locales/rails-i18n/vi.yml +198 -0
  124. data/locales/rails-i18n/wo.yml +198 -0
  125. data/locales/rails-i18n/zh-CN.yml +203 -0
  126. data/locales/rails-i18n/zh-HK.yml +204 -0
  127. data/locales/rails-i18n/zh-TW.yml +203 -0
  128. data/locales/rails-i18n/zh-YUE.yml +204 -0
  129. metadata +164 -74
@@ -15,8 +15,9 @@ module Amber
15
15
 
16
16
  def initialize(options)
17
17
  @site = options[:site]
18
+ @host = options[:host]
18
19
  @port = options[:port]
19
- @server = WEBrick::HTTPServer.new :Port => @port, :BindAddress => '127.0.0.1', :DocumentRoot => @site.dest_dir
20
+ @server = WEBrick::HTTPServer.new :Port => @port, :BindAddress => @host, :DocumentRoot => @site.dest_dir
20
21
  @server.mount '/', StaticPageServlet, self
21
22
  end
22
23
 
@@ -30,6 +31,9 @@ module Amber
30
31
 
31
32
  class StaticPageServlet < WEBrick::HTTPServlet::FileHandler
32
33
 
34
+ RENDERABLE_ASSET_RE = /(#{Amber::Render::Asset::SOURCE_MAP.keys.join('|')})$/
35
+ ASSET_RE = /\.(jpg|jpeg|png|gif|webm|css|js|ico)$/
36
+
33
37
  def initialize(http_server, amber_server)
34
38
  @logger = http_server.logger
35
39
  @server = amber_server
@@ -37,77 +41,139 @@ module Amber
37
41
  end
38
42
 
39
43
  def do_GET(request, response)
40
- dest_dir = @server.site.dest_dir
41
-
42
- # add local prefix for all paths that don't have a file suffix
43
- if request.path !~ /\.[a-z]{2,4}$/
44
- locale = get_locale(request)
45
- if !locale
46
- location = "http://localhost:#{@server.port}/#{I18n.default_locale}#{request.path.sub(/\/$/,'')}"
47
- @logger.info "Redirect %s ==> %s" % [request.path, location]
48
- response.header['Location'] = location
49
- response.status = 307
50
- return
51
- end
44
+ if path_needs_to_be_prefixed?(request.path)
45
+ redirect_with_prefix(request, response)
46
+ return
52
47
  end
53
48
 
54
- if File.exists?(File.join(dest_dir, request.path))
55
- @logger.info "Serve static file %s" % File.join(dest_dir, request.path)
49
+ path = strip_locale_and_prefix(request.path)
50
+
51
+ if renderable_asset?(path)
52
+ @logger.info "Rendering asset file %s" % asset_source_file(path)
53
+ render_asset(path, request, response)
56
54
  super(request, response)
55
+ elsif static_file_exists?(request.path)
56
+ @logger.info "Serve static file %s" % static_file_path(request.path)
57
+ super(request, response)
58
+ elsif static_file_exists?(path)
59
+ request = request.clone
60
+ request.instance_variable_set(:@path_info, "/"+path)
61
+ @logger.info "Serve static file with locale prefix %s" % static_file_path(path)
62
+ super(request, response)
63
+ elsif path !~ ASSET_RE
64
+ render_page(path, request, response)
57
65
  else
58
- path = strip_locale(request.path)
59
- @server.site.load_pages
60
- page = @server.site.find_page_by_path(path)
61
- if page
62
- @logger.info "Serving Page %s" % page.path.join('/')
63
- response.status = 200
64
- response.content_type = "text/html; charset=utf-8"
65
- # always refresh the page we are fetching
66
- Amber::Render::Layout.reload
67
- @server.site.render
68
- page.render_to_file(dest_dir, :force => true)
69
- file = page.destination_file(dest_dir, locale)
70
- if File.exists?(file)
71
- content = File.read(file)
72
- else
73
- file = page.destination_file(dest_dir, I18n.default_locale)
74
- if File.exists?(file)
75
- content = File.read(file)
76
- else
77
- view = Render::View.new(page, @server.site)
78
- content = view.render(:text => "No file found at #{file}")
79
- end
80
- end
81
- response.body = content
82
- else
83
- request = request.clone
84
- request.instance_variable_set(:@path_info, "/" + strip_locale(request.path_info))
85
- @logger.info "Serve static file %s" % File.join(dest_dir, request.path_info)
86
- super(request, response)
87
- end
66
+ super(request, response)
88
67
  end
89
68
  end
90
- #rescue Exception => exc
91
- # @logger.error exc.to_s
92
- # @logger.error exc.backtrace
93
- # response.status = 500
94
- # response.content_type = 'text/text'
95
- # response.body = exc.to_s + "\n\n\n\n" + exc.backtrace
96
69
 
97
70
  private
98
71
 
99
- def strip_locale(path)
100
- path.sub(/^\/(#{Amber::POSSIBLE_LANGUAGE_CODES.join('|')})(\/|$)/, '').sub(/\/$/, '')
72
+ def strip_locale_and_prefix(path)
73
+ # The path comes to the server as URL escaped codes, that are then
74
+ # converty to ascii. But these codes might be utf-8 characters, so we force
75
+ # utf-8 encouding to allows non-ascii paths. I am not sure if always forcing
76
+ # will be a problem.
77
+ path = path.force_encoding('utf-8')
78
+ if @server.site.path_prefix
79
+ path = path.sub(%r{^/?#{Regexp.escape(@server.site.path_prefix)}}, '')
80
+ end
81
+ path.sub(%r{^/?(#{Amber::POSSIBLE_LANGUAGE_CODES.join('|')})(/|$)}, '').sub(%r{/$}, '')
101
82
  end
102
83
 
103
- def get_locale(request)
104
- match = /\/(#{Amber::POSSIBLE_LANGUAGE_CODES.join('|')})(\/|$)/.match(request.path)
84
+ def get_locale(path)
85
+ match = /\/(#{Amber::POSSIBLE_LANGUAGE_CODES.join('|')})(\/|$)/.match(path)
105
86
  if match.nil?
106
87
  nil
107
88
  else
108
89
  match[1]
109
90
  end
110
91
  end
111
- end
112
92
 
93
+ def path_needs_to_be_prefixed?(path)
94
+ if @server.site.path_prefix
95
+ path !~ /\.[a-z]{2,4}$/ && (
96
+ path !~ %r{^/?#{Regexp.escape(@server.site.path_prefix)}} ||
97
+ get_locale(path).nil?
98
+ )
99
+ else
100
+ path !~ /\.[a-z]{2,4}$/ && get_locale(path).nil?
101
+ end
102
+ end
103
+
104
+ def redirect_with_prefix(request, response)
105
+ path = request.path.gsub(%r{^/|/$}, '')
106
+ location = ["http://localhost:#{@server.port}", @server.site.path_prefix, I18n.default_locale, path].compact.join('/')
107
+ @logger.info "Redirect %s ==> %s" % [request.path, location]
108
+ response.header['Location'] = location
109
+ response.status = 307
110
+ end
111
+
112
+ def dst_dir
113
+ @server.site.dest_dir
114
+ end
115
+
116
+ def src_dir
117
+ @server.site.pages_dir
118
+ end
119
+
120
+ def static_file_exists?(path)
121
+ File.file?(File.join(dst_dir, path))
122
+ end
123
+
124
+ def static_file_path(path)
125
+ File.join(dst_dir, path)
126
+ end
127
+
128
+ def render_page(path, request, response)
129
+ locale = get_locale(request.path)
130
+ @server.site.load_pages
131
+ page = @server.site.find_page_by_path(path, locale)
132
+ if page
133
+ @logger.info "Serving Page %s" % page.path.join('/')
134
+ response.status = 200
135
+ response.content_type = "text/html; charset=utf-8"
136
+ # always refresh the page we are fetching
137
+ Amber::Render::Layout.reload
138
+ @server.site.render
139
+ page.render_to_file(dst_dir, :force => true)
140
+ file = page.destination_file(dst_dir, locale)
141
+ if File.exists?(file)
142
+ content = File.read(file)
143
+ else
144
+ file = page.destination_file(dst_dir, I18n.default_locale)
145
+ if File.exists?(file)
146
+ content = File.read(file)
147
+ else
148
+ view = Render::View.new(page, @server.site)
149
+ content = view.render(:text => "No file found at #{file}")
150
+ end
151
+ end
152
+ response.body = content
153
+ end
154
+ end
155
+
156
+ def renderable_asset?(path)
157
+ path =~ RENDERABLE_ASSET_RE && asset_source_file(path)
158
+ end
159
+
160
+ def asset_source_file(path)
161
+ dest_suffix = File.extname(path)
162
+ base_path = path.sub(RENDERABLE_ASSET_RE, '')
163
+ Amber::Render::Asset::SOURCE_MAP[dest_suffix].each do |source_suffix|
164
+ source_file_path = File.join(src_dir, base_path + source_suffix)
165
+ if File.exists?(source_file_path)
166
+ return source_file_path
167
+ end
168
+ end
169
+ return nil
170
+ end
171
+
172
+ def render_asset(path, request, response)
173
+ src_file = asset_source_file(path)
174
+ dst_file = [dst_dir, path].join
175
+ Amber::Render::Asset.render(src_file, dst_file)
176
+ end
177
+
178
+ end
113
179
  end
@@ -9,32 +9,39 @@ module Amber
9
9
  attr_accessor :root
10
10
  attr_accessor :continue_on_error
11
11
 
12
- def_delegators :@config, :dest_dir, :locales, :default_locale
12
+ # @config is the primary SiteConfiguration
13
+ def_delegators :@config, :pages_dir, :dest_dir, :locales, :default_locale, :path_prefix
13
14
 
14
15
  def initialize(root_dir)
15
16
  @continue_on_error = true
16
17
  @config = SiteConfiguration.load(self, root_dir)
17
18
  end
18
19
 
20
+ def add_config(config)
21
+ @config.children << config
22
+ end
23
+
19
24
  def load_pages
20
25
  @root = nil
21
- @pages_by_path = {}
22
- @pages_by_name = {}
26
+ @pages_by_path = {} # hash of pages keyed by page path
27
+ @pages_by_name = {} # hash of pages keyed by page name
23
28
  @page_list = PageArray.new
24
- @dir_list = []
25
- @config.mount_points.each do |mp|
26
- add_mount_point(mp)
27
- mp.reset_timestamp
28
- end
29
- end
29
+ @dir_list = [] # an array of non-page directories
30
+ @page_paths = [] # an array of page paths, used for greping through paths.
30
31
 
31
- def reload_pages_if_needed
32
- if @pages_by_path.nil? || @config.pages_changed?
33
- puts "Reloading pages ................."
34
- load_pages
35
- end
32
+ # some paths are specific to only one locale (e.g aliases)
33
+ @pages_by_locale_path = POSSIBLE_LANGUAGES.keys.inject(Hash.new({})) {|h,locale| h[locale] = {}; h}
34
+
35
+ add_configuration(@config)
36
36
  end
37
37
 
38
+ #def reload_pages_if_needed
39
+ # if @pages_by_path.nil? || @config.pages_changed?
40
+ # puts "Reloading pages ................."
41
+ # load_pages
42
+ # end
43
+ #end
44
+
38
45
  def render
39
46
  @page_list.each do |page|
40
47
  page.render_to_file(@config.dest_dir)
@@ -46,6 +53,10 @@ module Amber
46
53
  Render::Asset.render_dir(src, dst)
47
54
  putc '.'; $stdout.flush
48
55
  end
56
+ if @config.short_paths
57
+ render_short_path_symlinks
58
+ end
59
+ Render::Apache.write_htaccess(@config, @config.pages_dir, @config.dest_dir)
49
60
  puts
50
61
  end
51
62
 
@@ -62,6 +73,13 @@ module Amber
62
73
  @config.dest_dir = dest_dir
63
74
  end
64
75
 
76
+ #
77
+ # find pages by a filter.
78
+ # filter is a string composing a path segment.
79
+ # For example:
80
+ # "chat/security"
81
+ # Which would match "/services/chat/security" but not "/services/security"
82
+ #
65
83
  def find_pages(filter)
66
84
  filter = filter.downcase
67
85
  if filter =~ /\//
@@ -89,54 +107,100 @@ module Amber
89
107
  @page_list
90
108
  end
91
109
 
92
- def find_page_by_path(path)
93
- @pages_by_path[path]
110
+ def find_page_by_path(path, locale=I18n.default_locale)
111
+ if locale.is_a? String
112
+ if I18n.locale_available?(locale)
113
+ locale = locale.to_sym
114
+ end
115
+ end
116
+ if path.is_a? Array
117
+ path = path.join('/')
118
+ end
119
+ @pages_by_locale_path[locale][path] || @pages_by_path[path]
94
120
  end
95
121
 
96
122
  def find_page_by_name(name)
97
123
  @pages_by_name[name]
98
124
  end
99
125
 
126
+ #
127
+ # returns the shortest possible path for a page, such that the path doesn't collide with
128
+ # another page or another page's aliases.
129
+ #
130
+ def shortest_path(page)
131
+ path_so_far = []
132
+ page.path.reverse.each do |path_segment|
133
+ path_so_far.push(path_segment)
134
+ path_str = path_so_far.join('/')
135
+ if @pages_by_path[path_str].nil? && short_paths[path_str] == page
136
+ return path_so_far
137
+ end
138
+ end
139
+ return []
140
+ end
141
+
100
142
  private
101
143
 
102
- def add_mount_point(mp)
144
+ def add_configuration(config)
145
+ config.reset_timestamp
146
+
103
147
  # create base_page
104
148
  base_page = begin
105
- if mp.path == '/'
106
- @root = StaticPage.new(nil, 'root', mp.pages_dir)
149
+ if config.path.nil?
150
+ @root = StaticPage.new(nil, 'root', config.pages_dir)
107
151
  add_page(@root)
108
152
  @root
109
153
  else
110
- name = File.basename(mp.path)
111
- page = StaticPage.new(find_parent(mp.path), name, mp.pages_dir)
154
+ name = File.basename(config.path)
155
+ page = StaticPage.new(find_parent(config.path), name, config.pages_dir, config.path_prefix)
112
156
  add_page(page)
113
157
  page
114
158
  end
115
159
  end
116
- base_page.mount_point = mp
160
+ base_page.config = config
117
161
 
118
162
  # load menu and locals
119
- I18n.load_path += Dir[File.join(mp.locales_dir, '/*.{rb,yml,yaml}')] if mp.locales_dir
163
+ I18n.load_path += Dir[File.join(config.locales_dir, '/*.{rb,yml,yaml}')] if config.locales_dir
120
164
 
121
165
  # add the full directory tree
122
166
  base_page.scan_directory_tree do |page, asset_dir|
123
167
  add_page(page) if page
124
168
  @dir_list << asset_dir if asset_dir
125
169
  end
126
- @page_paths = @pages_by_path.keys
170
+ @page_paths += @pages_by_path.keys
171
+
172
+ # recursively add sub-sites
173
+ config.children.each do |sub_config|
174
+ add_configuration(sub_config)
175
+ end
127
176
  end
128
177
 
178
+ #
179
+ # registers a page with the site, indexing the page path in our various hashes
180
+ #
129
181
  def add_page(page)
130
182
  @pages_by_name[page.name] = page
131
183
  @pages_by_path[page.path.join('/')] = page
132
- page.aliases.each do |alias_path|
133
- if @pages_by_path[alias_path]
134
- Amber.logger.warn "WARNING: page `#{page.path.join('/')}` has alias `#{alias_path}`, but this path is already taken"
184
+ add_aliases(I18n.default_locale, page, @pages_by_path)
185
+ page.locales.each do |locale|
186
+ next if locale == I18n.default_locale
187
+ add_aliases(locale, page, @pages_by_locale_path[locale])
188
+ end
189
+ @page_list << page
190
+ end
191
+
192
+ #
193
+ # registers a page's aliases with the site
194
+ #
195
+ def add_aliases(locale, page, path_hash)
196
+ page.aliases(locale).each do |alias_path|
197
+ alias_path_str = alias_path.join('/')
198
+ if path_hash[alias_path_str]
199
+ Amber.logger.warn "WARNING: page `#{page.path.join('/')}` has alias `#{alias_path_str}`, but this path is already taken by `#{path_hash[alias_path_str].path.join('/')}` (locale = #{locale})."
135
200
  else
136
- @pages_by_path[alias_path] = page
201
+ path_hash[alias_path_str] = page
137
202
  end
138
203
  end
139
- @page_list << page
140
204
  end
141
205
 
142
206
  def find_parent(path)
@@ -150,5 +214,83 @@ module Amber
150
214
  return @root
151
215
  end
152
216
 
217
+ #
218
+ # adds symlinks for all possible 'short paths' for every page.
219
+ # this works by:
220
+ # (1) examine all pages in order of longest path depth and assign 'short paths' for each page.
221
+ # (2) examine all short paths in order of shortest path depth and create symlinks
222
+ #
223
+ def render_short_path_symlinks
224
+ short_paths.each do |path, page|
225
+ page.link_page_aliases(@config.dest_dir, [path])
226
+ end
227
+ end
228
+
229
+ #
230
+ # returns a hash containing all the automatically determined shortest paths for every page.
231
+ # the data structure looks like so:
232
+ #
233
+ # {
234
+ # "ddd" => <page 'bbb/ddd'>,
235
+ # "ccc" => <page 'bbb/ccc'>,
236
+ # "red" => <page 'autoalias/red'>,
237
+ # "blue"=> <page 'autoalias/blue'>,
238
+ # "red/blue" => <page 'autoalias/red/blue'>
239
+ # }
240
+ #
241
+ # short_paths does not include the normal paths or normal aliases, just the automatic short path aliases.
242
+ #
243
+ def short_paths
244
+ @short_paths ||= begin
245
+ hash = {}
246
+ pages_in_path_depth_order.each do |record|
247
+ page = record[:page]
248
+ path = record[:path]
249
+ next if path.length == 1
250
+ path_prefix = path.dup
251
+ path.length.times do |depth|
252
+ path_prefix.shift
253
+ path_str = path_prefix.join('/')
254
+ if @pages_by_path[path_str].nil? && hash[path_str].nil?
255
+ hash[path_str] = page
256
+ end
257
+ end
258
+ end
259
+ # debug:
260
+ #hash.each do |path, record|
261
+ # puts "#{record[:page].path.join('/')} => #{record[:path].join('/')}"
262
+ #end
263
+ hash
264
+ end
265
+ end
266
+
267
+ #
268
+ # Returns an array like this:
269
+ #
270
+ # [
271
+ # {:page => <page1>, :path => ['a', 'page1']},
272
+ # {:page => <page2>, :path => ['a','b', 'page2']},
273
+ # ]
274
+ #
275
+ # This array is sorted by the depth of the path (shortest first)
276
+ # Pages will appear multiple times (once for each path, including aliases)
277
+ #
278
+ def pages_in_path_depth_order
279
+ paths = {}
280
+ @page_list.each do |page|
281
+ paths[page.path] ||= page
282
+ locales = page.locales
283
+ locales << I18n.default_locale unless locales.include? I18n.default_locale
284
+ locales.each do |locale|
285
+ page.aliases(locale).each do |alias_path|
286
+ paths[alias_path] ||= page
287
+ end
288
+ end
289
+ end
290
+ paths.collect{|path, page| {page:page, path:path}}.sort{|a,b|
291
+ a[:path].length <=> a[:path].length
292
+ }
293
+ end
294
+
153
295
  end
154
296
  end