amber 0.3.8 → 0.3.11

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4d45088b2c65fd274e44926886b8a70f0a645f73
4
- data.tar.gz: 305ef95bcb2143c71331cfe7e702b97ee9f987f1
3
+ metadata.gz: 5f07ec2e5c2a86cec442c924598f935a40955a32
4
+ data.tar.gz: 4eaecfe55678b175942014c5e17f6e90381021b6
5
5
  SHA512:
6
- metadata.gz: 5b4fc64f2b1f57cafa02c21d2392448e64de89648a9ebe54ae06b26b43febcbf5a5b663804c3b7ff3932c5553077ae82be017490ed922ba8571e5c0aaa6ba706
7
- data.tar.gz: a6ab0937bad9f67492940655bfbcf1e04e9b79c48e5a9ec5761686b25a39d1c2165d0b54d19f76cf470e6e14e8b55edc30b61a446f4ca22ea8a38f49fd423f44
6
+ metadata.gz: efa8c04a618b8ba9052c6b4f0f1387bb750e20a3544e6afe79668cee9de9278c44168c2ce00fe70fc14c8dc4a3faebdb6a5b069e059cd913adc37373f8ade543
7
+ data.tar.gz: 78c14db10c74f733f0cc1899715ccbf771a3bd9b29aebdf84af8d24bd0025845ced2b0df3adfa5c7237e63619defe0932eae0280561f66f1a0051f8d8c21d3b7
@@ -21,6 +21,10 @@ module Amber
21
21
  # Possible page suffixes. Only files with these suffixes are treated as pages
22
22
  PAGE_SUFFIXES = %w(haml md markdown text textile rst html html.haml)
23
23
 
24
+ # Possible variable file suffixes. Only files with these suffixesare treated
25
+ # as variable files.
26
+ VAR_SUFFIXES = %w(json yaml yml)
27
+
24
28
  DEFAULT_HOST = '127.0.0.1'
25
29
  DEFAULT_PORT = '8000'
26
30
 
@@ -56,7 +60,8 @@ require 'amber/render/layout'
56
60
  require 'amber/render/view'
57
61
  require 'amber/render/template'
58
62
  require 'amber/render/asset'
59
- require 'amber/render/autolink'
60
- require 'amber/render/bracketlink'
61
63
  require 'amber/render/table_of_contents'
62
64
  require 'amber/render/apache'
65
+ require 'amber/render/filter/autolink'
66
+ require 'amber/render/filter/bracketlink'
67
+ require 'amber/render/filter/variables'
@@ -13,7 +13,7 @@ module Amber
13
13
  new_dir = options[:arg]
14
14
  mkdir(new_dir, nil)
15
15
  mkdir('amber', new_dir)
16
- touch('amber/config.rb', new_dir)
16
+ copy_template_file('config.rb', 'amber', new_dir)
17
17
  touch('amber/menu.txt', new_dir)
18
18
  mkdir('amber/layouts', new_dir)
19
19
  mkdir('amber/locales', new_dir)
@@ -40,8 +40,8 @@ module Amber
40
40
  site = Site.new(@root)
41
41
  site.continue_on_error = false
42
42
  site.load_pages
43
- FileUtils.mkdir_p(site.dest_dir) unless File.exists?(site.dest_dir)
44
- gitkeep = File.exists?(File.join(site.dest_dir, '.gitkeep'))
43
+ FileUtils.mkdir_p(site.dest_dir) unless File.exist?(site.dest_dir)
44
+ gitkeep = File.exist?(File.join(site.dest_dir, '.gitkeep'))
45
45
  temp_render = File.join(File.dirname(site.dest_dir), 'public-tmp')
46
46
  temp_old_pages = File.join(File.dirname(site.dest_dir), 'remove-me')
47
47
  site.with_destination(temp_render) do
@@ -58,8 +58,8 @@ module Amber
58
58
  end
59
59
  ensure
60
60
  # cleanup if something goes wrong.
61
- FileUtils.rm_r(temp_render) if temp_render && File.exists?(temp_render)
62
- FileUtils.rm_r(temp_old_pages) if temp_old_pages && File.exists?(temp_old_pages)
61
+ FileUtils.rm_r(temp_render) if temp_render && File.exist?(temp_render)
62
+ FileUtils.rm_r(temp_old_pages) if temp_old_pages && File.exist?(temp_old_pages)
63
63
  end
64
64
 
65
65
  def server(options)
@@ -100,8 +100,8 @@ module Amber
100
100
  path = dir
101
101
  print_path = dir
102
102
  end
103
- unless Dir.exists?(path)
104
- if File.exists?(path)
103
+ unless Dir.exist?(path)
104
+ if File.exist?(path)
105
105
  puts "Could not make directory `#{print_path}`. File already exists."
106
106
  exit(1)
107
107
  end
@@ -112,11 +112,22 @@ module Amber
112
112
 
113
113
  def touch(file, context)
114
114
  path = File.join(context, file)
115
- unless File.exists?(path)
115
+ unless File.exist?(path)
116
116
  FileUtils.touch(path)
117
117
  puts "* Creating `#{File.basename(context)}/#{file}`"
118
118
  end
119
119
  end
120
120
 
121
+ def copy_template_file(template_file, destination, context)
122
+ template_file_path = File.dirname(__FILE__) + "/templates/#{template_file}"
123
+ copy(template_file_path, destination, context)
124
+ end
125
+
126
+ def copy(source, destination, context)
127
+ destination_path = File.join(context, destination)
128
+ FileUtils.copy(source, destination_path)
129
+ puts "* Creating `#{File.basename(context)}/#{destination}/#{File.basename(source)}`"
130
+ end
131
+
121
132
  end
122
- end
133
+ end
@@ -8,7 +8,7 @@ module Amber
8
8
  template = Tilt::ERBTemplate.new(template_path("htaccess.erb"))
9
9
 
10
10
  tail_content = nil
11
- if File.exists?(src_htaccess_file)
11
+ if File.exist?(src_htaccess_file)
12
12
  tail_content = File.read(src_htaccess_file)
13
13
  end
14
14
  File.open(dst_htaccess_file, 'w', :encoding => 'UTF-8') do |f|
@@ -19,10 +19,10 @@ module Amber
19
19
  }
20
20
 
21
21
  def self.render(src_file, dst_file)
22
- unless Dir.exists?(File.dirname(dst_file))
22
+ unless Dir.exist?(File.dirname(dst_file))
23
23
  FileUtils.mkdir_p(File.dirname(dst_file))
24
24
  end
25
- File.unlink(dst_file) if File.exists?(dst_file)
25
+ File.unlink(dst_file) if File.exist?(dst_file)
26
26
  src_ext = File.extname(src_file)
27
27
  renderer = RENDER_MAP[src_ext]
28
28
  if renderer
@@ -0,0 +1,84 @@
1
+ # encoding: utf-8
2
+ # adapted from https://github.com/tenderlove/rails_autolink
3
+ # MIT license
4
+
5
+ module Amber
6
+ module Render
7
+ module Filter
8
+ module Autolink
9
+
10
+ def self.run(text)
11
+ auto_link_email_addresses(auto_link_urls(text))
12
+ end
13
+
14
+ private
15
+
16
+ AUTO_LINK_RE = %r{
17
+ (?: ((?:ed2k|ftp|http|https|irc|mailto|news|gopher|nntp|telnet|webcal|xmpp|callto|feed|svn|urn|aim|rsync|tag|ssh|sftp|rtsp|afs|file):)// | www\. )
18
+ [^\s<\u00A0]+
19
+ }ix
20
+
21
+ # regexps for determining context, used high-volume
22
+ AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, /<a\b.*?>/i, /<\/a>/i]
23
+
24
+ AUTO_EMAIL_LOCAL_RE = /[\w.!#\$%&'*\/=?^`{|}~+-]/
25
+ AUTO_EMAIL_RE = /[\w.!#\$%+-]\.?#{AUTO_EMAIL_LOCAL_RE}*@[\w-]+(?:\.[\w-]+)+/
26
+
27
+ BRACKETS = { ']' => '[', ')' => '(', '}' => '{' }
28
+
29
+ WORD_PATTERN = RUBY_VERSION < '1.9' ? '\w' : '\p{Word}'
30
+
31
+ # Turns all urls into clickable links. If a block is given, each url
32
+ # is yielded and the result is used as the link text.
33
+ def self.auto_link_urls(text)
34
+ text.gsub(AUTO_LINK_RE) do
35
+ scheme, href = $1, $&
36
+ punctuation = []
37
+
38
+ if auto_linked?($`, $')
39
+ # do not change string; URL is already linked
40
+ href
41
+ else
42
+ # don't include trailing punctuation character as part of the URL
43
+ while href.sub!(/[^#{WORD_PATTERN}\/-]$/, '')
44
+ punctuation.push $&
45
+ if opening = BRACKETS[punctuation.last] and href.scan(opening).size > href.scan(punctuation.last).size
46
+ href << punctuation.pop
47
+ break
48
+ end
49
+ end
50
+
51
+ #link_text = block_given?? yield(href) : href
52
+ link_text = href.sub(/^#{scheme}\/\//,'')
53
+ href = 'http://' + href unless scheme
54
+ %(<a href="#{href}">#{link_text}</a>) + punctuation.reverse.join('')
55
+ end
56
+ end
57
+ end
58
+
59
+ # Turns all email addresses into clickable links.
60
+ def self.auto_link_email_addresses(text)
61
+ text.gsub(AUTO_EMAIL_RE) do
62
+ text = $&
63
+
64
+ if auto_linked?($`, $')
65
+ text
66
+ else
67
+ #display_text = (block_given?) ? yield(text) : text
68
+ #display_text = text
69
+ text.gsub!('@', '&#064').gsub!('.', '&#046;')
70
+ %(<a href="mailto:#{text}">#{text}</a>)
71
+ end
72
+ end
73
+ end
74
+
75
+ # Detects already linked context or position in the middle of a tag
76
+ def self.auto_linked?(left, right)
77
+ (left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or
78
+ (left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3])
79
+ end
80
+
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+
3
+ #
4
+ # bracket links are links in the form [[label => target]] or [[page-name]]
5
+ #
6
+
7
+ module Amber
8
+ module Render
9
+ module Filter
10
+ module Bracketlink
11
+
12
+ # linking using double square brackets
13
+ BRACKET_LINK_RE = /
14
+ \[\[ # start [[
15
+ ([^\[\]]+) # $text : one or more characters that are not [ or ] ($1)
16
+ \]\] # end ]]
17
+ /x
18
+
19
+ def self.run(text, &block)
20
+ text.gsub(BRACKET_LINK_RE) do |m|
21
+ link_text = $~[1].strip
22
+ if link_text =~ /^.+\s*[-=]>\s*.+$/
23
+ # link_text == "from -> to"
24
+ from, to = link_text.split(/\s*[-=]>\s*/)[0..1]
25
+ from = "" unless from.instance_of? String # \ sanity check for
26
+ to = "" unless from.instance_of? String # / badly formed links
27
+ else
28
+ # link_text == "to" (ie, no link label)
29
+ from = nil
30
+ to = link_text
31
+ end
32
+ yield(from, to)
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ #
4
+ # bracket links are links in the form [[label => target]] or [[page-name]]
5
+ #
6
+
7
+ module Amber
8
+ module Render
9
+ module Filter
10
+ module Variables
11
+
12
+ # variable expansion uses {{ }}
13
+ VARIABLES_RE = /
14
+ \{\{ # start {{
15
+ ([^\{\}]+) # $text : one or more characters that are not { or } ($1)
16
+ \}\} # end }}
17
+ /x
18
+
19
+ def self.run(text, &block)
20
+ text.gsub(VARIABLES_RE) do |m|
21
+ variable_name = $~[1].strip
22
+ yield(variable_name)
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -6,7 +6,7 @@ module Amber
6
6
  limit = options[:limit] || @site.pagination_size
7
7
  order = options[:order] || :posted_at
8
8
  direction = options[:direction] || :desc
9
- partial = options[:partial]
9
+ #partial = options[:partial]
10
10
  if options[:path]
11
11
  @site.find_page(options[:path])
12
12
  else
@@ -108,7 +108,7 @@ module Amber
108
108
  levels_max = options[:levels] || 1
109
109
  level = options.delete(:level) || 1
110
110
  heading = options.delete(:heading) || 2
111
- locale = @locals[:locale]
111
+ #locale = @locals[:locale]
112
112
  menu = submenu_for_page(page)
113
113
  if menu && menu.children.any?
114
114
  children = menu.children
@@ -27,6 +27,7 @@ module Amber::Render
27
27
  @heading_anchors = {}
28
28
  @options = options
29
29
  @options[:tag] ||= 'ol'
30
+ @parsed = nil
30
31
  end
31
32
 
32
33
  def to_html
@@ -125,7 +125,10 @@ module Amber
125
125
 
126
126
  def render_haml(file_path, view)
127
127
  template = Tilt::HamlTemplate.new(file_path, {:format => :html5, :default_encoding => 'UTF-8'})
128
- add_bracket_links(view, template.render(view, view.locals))
128
+ html = template.render(view, view.locals)
129
+ html = add_variables(view, html)
130
+ html = add_bracket_links(view, html)
131
+ return html
129
132
  rescue Haml::Error => ex
130
133
  msg = "Line #{ex.line + 1} of file `#{file_path}`: #{ex.to_s}"
131
134
  Amber::logger.error(msg)
@@ -133,34 +136,46 @@ module Amber
133
136
  end
134
137
 
135
138
  def render_textile(view, content)
139
+ content = add_variables(view, content)
136
140
  content = add_bracket_links(view, content)
137
- Autolink.auto_link(RedCloth.new(content).to_html)
141
+ return Filter::Autolink.run(RedCloth.new(content).to_html)
138
142
  end
139
143
 
140
144
  def render_markdown(view, content)
145
+ content = add_variables(view, content)
141
146
  content = add_bracket_links(view, content)
142
- RDiscount.new(content, :smart, :autolink).to_html
147
+ return RDiscount.new(content, :smart, :autolink).to_html
143
148
  end
144
149
 
145
150
  def render_raw(view, content)
146
- add_bracket_links(view, content)
151
+ content = add_variables(view, content)
152
+ content = add_bracket_links(view, content)
153
+ return content
147
154
  end
148
155
 
149
156
  def render_none(view, content)
150
- content
157
+ return content
151
158
  end
152
159
 
153
160
  def add_bracket_links(view, content)
154
- content = Bracketlink.bracket_link(content) do |from, to|
161
+ content = Filter::Bracketlink.run(content) do |from, to|
155
162
  view.link({from => to})
156
163
  end
157
- content
164
+ return content
165
+ end
166
+
167
+ def add_variables(view, content)
168
+ locale = view.locals[:locale]
169
+ content = Filter::Variables.run(content) do |var_name|
170
+ view.page.var(var_name, locale) || var_name
171
+ end
172
+ return content
158
173
  end
159
174
 
160
175
  def type_from_file(file_path)
161
176
  suffix = File.extname(file_path)
162
177
  if suffix
163
- suffix.sub! /^\./, ''
178
+ suffix.sub!(/^\./, '')
164
179
  suffix = suffix.to_sym
165
180
  end
166
181
  suffix
@@ -57,6 +57,7 @@ module Amber
57
57
  @page = options[:page] if options[:page]
58
58
  render_toc = should_render_toc?(locale, options, @page)
59
59
  template = pick_template(locale, options)
60
+ return unless template
60
61
  if toc_only
61
62
  template.render(self, :mode => :toc, :href_base => options[:href_base])
62
63
  else
@@ -90,32 +91,33 @@ module Amber
90
91
  # search possible paths for the file to be rendered.
91
92
  # called only from parse_render_options()
92
93
  #
93
- def find_file(path, site, page, locale)
94
- return path if File.exists?(path)
95
- search = [
96
- path,
97
- "#{site.pages_dir}/#{path}",
98
- "#{page.file_path}/#{path}",
99
- "#{File.dirname(page.file_path)}/#{path}",
100
- "#{site.config_dir}/#{path}"
94
+ def find_file(search_path, site, page, locale)
95
+ return search_path if File.exist?(search_path)
96
+ searches = [
97
+ search_path,
98
+ "#{site.pages_dir}/#{search_path}",
99
+ "#{page.file_path}/#{search_path}",
100
+ "#{File.dirname(page.file_path)}/#{search_path}",
101
+ "#{site.config_dir}/#{search_path}"
101
102
  ]
102
103
  # attempt to find a file with preferred locale
103
- search.each do |path|
104
- return path if File.exists?(path)
104
+ searches.each do |path|
105
+ return path if File.exist?(path)
105
106
  Dir["#{path}.#{locale}.#{StaticPage::PAGE_SUFFIXES_GLOB}"].each do |path_with_locale|
106
- return path_with_locale if File.exists?(path_with_locale)
107
+ return path_with_locale if File.exist?(path_with_locale)
107
108
  end
108
109
  Dir["#{path}.#{StaticPage::PAGE_SUFFIXES_GLOB}"].each do |path_with_suffix|
109
- return path_with_suffix if File.exists?(path_with_suffix)
110
+ return path_with_suffix if File.exist?(path_with_suffix)
110
111
  end
111
112
  end
112
113
  # attempt to find a file with default locale
113
- search.each do |path|
114
- Dir["#{path}.#{I18n.default_locale}.#{StaticPage::PAGE_SUFFIXES_GLOB}"].each do |path_with_locale|
115
- return path_with_locale if File.exists?(path_with_locale)
114
+ searches.each do |path2|
115
+ Dir["#{path2}.#{I18n.default_locale}.#{StaticPage::PAGE_SUFFIXES_GLOB}"].each do |path_with_locale|
116
+ return path_with_locale if File.exist?(path_with_locale)
116
117
  end
117
118
  end
118
- raise MissingTemplate.new(path)
119
+ Amber.logger.error("No such path `#{path2}` from `#{page.content_file(locale)}`.")
120
+ return nil
119
121
  end
120
122
 
121
123
  def partialize(path)
@@ -181,6 +183,8 @@ module Amber
181
183
  Template.new(file: options[:partial], partial: true)
182
184
  elsif options[:text]
183
185
  Template.new(content: options[:text], type: (options[:type] || :text))
186
+ else
187
+ nil
184
188
  end
185
189
  end
186
190
 
@@ -138,11 +138,11 @@ module Amber
138
138
  @server.site.render
139
139
  page.render_to_file(dst_dir, :force => true)
140
140
  file = page.destination_file(dst_dir, locale)
141
- if File.exists?(file)
141
+ if File.exist?(file)
142
142
  content = File.read(file)
143
143
  else
144
144
  file = page.destination_file(dst_dir, I18n.default_locale)
145
- if File.exists?(file)
145
+ if File.exist?(file)
146
146
  content = File.read(file)
147
147
  else
148
148
  view = Render::View.new(page, @server.site)
@@ -162,7 +162,7 @@ module Amber
162
162
  base_path = path.sub(RENDERABLE_ASSET_RE, '')
163
163
  Amber::Render::Asset::SOURCE_MAP[dest_suffix].each do |source_suffix|
164
164
  source_file_path = File.join(src_dir, base_path + source_suffix)
165
- if File.exists?(source_file_path)
165
+ if File.exist?(source_file_path)
166
166
  return source_file_path
167
167
  end
168
168
  end
@@ -81,8 +81,8 @@ module Amber
81
81
  # Which would match "/services/chat/security" but not "/services/security"
82
82
  #
83
83
  def find_pages(filter)
84
- filter = filter.downcase
85
- if filter =~ /\//
84
+ filter = filter.downcase
85
+ if filter =~ /\//
86
86
  path = filter.split('/').map{|segment| segment.gsub(/[^0-9a-z_-]/, '')}
87
87
  path_str = path.join('/')
88
88
  if (page = @pages_by_path[path_str])
@@ -152,9 +152,9 @@ module Amber
152
152
  @root
153
153
  else
154
154
  name = File.basename(config.path)
155
- page = StaticPage.new(find_parent(config.path), name, config.pages_dir, config.path_prefix)
156
- add_page(page)
157
- page
155
+ sub_root = StaticPage.new(find_parent(config.path), name, config.pages_dir, config.path_prefix)
156
+ add_page(sub_root)
157
+ sub_root
158
158
  end
159
159
  end
160
160
  base_page.config = config
@@ -56,6 +56,7 @@ module Amber
56
56
  #
57
57
  def initialize(site, root_dir, options={})
58
58
  @children = []
59
+ @path_prefix = nil
59
60
  @site = site
60
61
  @root_dir = File.expand_path(find_in_directory_tree('amber', 'config.rb', root_dir))
61
62
  if @root_dir == '/'
@@ -119,7 +120,7 @@ module Amber
119
120
 
120
121
  def config_path(file)
121
122
  path = File.join(@config_dir, file)
122
- if File.exists?(path)
123
+ if File.exist?(path)
123
124
  path
124
125
  else
125
126
  nil
@@ -134,7 +135,7 @@ module Amber
134
135
  search_dir = directory_tree || Dir.pwd
135
136
  while search_dir != "/"
136
137
  Dir.foreach(search_dir) do |f|
137
- if f == target_dir_name && File.exists?(File.join(search_dir, f,target_file_name))
138
+ if f == target_dir_name && File.exist?(File.join(search_dir, f,target_file_name))
138
139
  return search_dir
139
140
  end
140
141
  end
@@ -0,0 +1,95 @@
1
+ #
2
+ # responsible for loading all the pages of the site.
3
+ #
4
+
5
+ WORK IN PROGRESS, FACTOR OUT FROM SITE
6
+
7
+ module Amber
8
+ class SiteLoader
9
+
10
+ #
11
+ # page_paths
12
+ # pages_by_name
13
+ # pages_by_path
14
+ # pages_by_locale_path
15
+ # root
16
+ # dir_list
17
+ #
18
+ def self.load(config)
19
+ config.reset_timestamp
20
+
21
+ # create base_page
22
+ base_page = begin
23
+ if config.path.nil?
24
+ @root = StaticPage.new(nil, 'root', config.pages_dir)
25
+ add_page(@root)
26
+ @root
27
+ else
28
+ name = File.basename(config.path)
29
+ page = StaticPage.new(find_parent(config.path), name, config.pages_dir, config.path_prefix)
30
+ add_page(page)
31
+ page
32
+ end
33
+ end
34
+ base_page.config = config
35
+
36
+ # load menu and locals
37
+ I18n.load_path += Dir[File.join(config.locales_dir, '/*.{rb,yml,yaml}')] if config.locales_dir
38
+
39
+ # add the full directory tree
40
+ base_page.scan_directory_tree do |page, asset_dir|
41
+ add_page(page) if page
42
+ @dir_list << asset_dir if asset_dir
43
+ end
44
+ @page_paths += @pages_by_path.keys
45
+
46
+ # recursively add sub-sites
47
+ config.children.each do |sub_config|
48
+ add_configuration(sub_config)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def self.find_parent(path)
55
+ so_far = []
56
+ path.split('/').compact.each do |path_segment|
57
+ so_far << path_segment
58
+ if page = @pages_by_path[so_far.join('/')]
59
+ return page
60
+ end
61
+ end
62
+ return @root
63
+ end
64
+
65
+ #
66
+ # registers a page with the site, indexing the page path in our various hashes
67
+ #
68
+ def add_page(page)
69
+ @pages_by_name[page.name] = page
70
+ @pages_by_path[page.path.join('/')] = page
71
+ add_aliases(I18n.default_locale, page, @pages_by_path)
72
+ page.locales.each do |locale|
73
+ next if locale == I18n.default_locale
74
+ add_aliases(locale, page, @pages_by_locale_path[locale])
75
+ end
76
+ @page_list << page
77
+ end
78
+
79
+ #
80
+ # registers a page's aliases with the site
81
+ #
82
+ def add_aliases(locale, page, path_hash)
83
+ page.aliases(locale).each do |alias_path|
84
+ alias_path_str = alias_path.join('/')
85
+ if path_hash[alias_path_str]
86
+ 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})."
87
+ else
88
+ path_hash[alias_path_str] = page
89
+ end
90
+ end
91
+ end
92
+
93
+
94
+ end
95
+ end
@@ -112,6 +112,14 @@ module Amber
112
112
  @props.prop(*args)
113
113
  end
114
114
 
115
+ def vars
116
+ @vars ||= load_variables
117
+ end
118
+
119
+ def var(name, locale=I18n.locale)
120
+ (vars[locale] || vars[I18n.default_locale] || {})[name.to_s]
121
+ end
122
+
115
123
  #
116
124
  # Returns array of locale symbols for all locales with properties set
117
125
  # Note: there might be a content for a locale that does not show up in this array,
@@ -121,6 +129,10 @@ module Amber
121
129
  @props.locales
122
130
  end
123
131
 
132
+ def path_str
133
+ self.path.join('/')
134
+ end
135
+
124
136
  #
125
137
  # returns an array of normalized aliases based on the :alias property
126
138
  # defined for a page.
@@ -132,9 +144,9 @@ module Amber
132
144
  def aliases(locale=I18n.default_locale)
133
145
  @aliases ||= begin
134
146
  aliases_hash = Hash.new([])
135
- @props.locales.each do |locale|
136
- aliases = @props.prop_without_inheritance(locale, :alias)
137
- aliases_hash[locale] = begin
147
+ @props.locales.each do |l|
148
+ aliases = @props.prop_without_inheritance(l, :alias)
149
+ aliases_hash[l] = begin
138
150
  if aliases.nil?
139
151
  []
140
152
  else
@@ -5,6 +5,8 @@
5
5
  require 'i18n'
6
6
  require 'pathname'
7
7
  require 'fileutils'
8
+ require 'json'
9
+ require 'yaml'
8
10
 
9
11
  module Amber
10
12
  class StaticPage
@@ -106,11 +108,19 @@ module Amber
106
108
  PAGE_SUFFIXES_RE = /(?<suffix>#{Amber::PAGE_SUFFIXES.join('|')})/
107
109
  PAGE_SUFFIXES_GLOB = "{#{Amber::PAGE_SUFFIXES.join(',')}}"
108
110
 
111
+ # e.g. json, yaml
112
+ VAR_SUFFIXES_RE = /(?<suffix>#{Amber::VAR_SUFFIXES.join('|')})/
113
+ VAR_SUFFIXES_GLOB = "{#{Amber::VAR_SUFFIXES.join(',')}}"
114
+
109
115
  # e.g. en.haml or es.md or index.pt.text
110
116
  LOCALE_FILE_MATCH_RE = /^(index\.)?#{LOCALES_RE}\.#{PAGE_SUFFIXES_RE}$/
111
117
  LOCALE_FILE_MATCH_GLOB = "{index.,}#{LOCALES_GLOB}.#{PAGE_SUFFIXES_GLOB}"
112
118
 
119
+ VAR_FILE_MATCH_RE = /^(index\.)?#{LOCALES_RE}\.#{VAR_SUFFIXES_RE}$/
120
+ VAR_FILE_MATCH_GLOB = "{index.,}#{LOCALES_GLOB}.#{VAR_SUFFIXES_GLOB}"
121
+
113
122
  SIMPLE_FILE_MATCH_RE = lambda {|name| /^(#{Regexp.escape(name)})(\.#{LOCALES_RE})?\.#{PAGE_SUFFIXES_RE}$/ }
123
+ SIMPLE_VAR_MATCH_RE = lambda {|name| /^(#{Regexp.escape(name)})(\.#{LOCALES_RE})?\.#{VAR_SUFFIXES_RE}$/ }
114
124
 
115
125
  private
116
126
 
@@ -198,11 +208,13 @@ module Amber
198
208
  if @simple_page
199
209
  []
200
210
  else
201
- assets = {}
202
211
  Dir.foreach(@file_path).collect { |file|
203
- if file && file !~ /\.#{PAGE_SUFFIXES_RE}$/
204
- file unless File.directory?(File.join(@file_path, file))
205
- end
212
+ is_asset = \
213
+ file &&
214
+ file !~ /\.#{PAGE_SUFFIXES_RE}$/ &&
215
+ file !~ /^#{VAR_FILE_MATCH_RE}$/ &&
216
+ !File.directory?(File.join(@file_path, file))
217
+ file if is_asset
206
218
  }.compact
207
219
  end
208
220
  end
@@ -223,24 +235,22 @@ module Amber
223
235
  #
224
236
  # (with or without leading hypen works)
225
237
  #
226
- # this text is extracted and evaluated as ruby to set properties.
238
+ # This text is extracted and evaluated as ruby to set properties.
239
+ #
240
+ # The first paragraph is loaded into the property "excerpt".
227
241
  #
228
242
  def load_properties
229
243
  props = PageProperties.new(self)
230
244
  content_files.each do |locale, content_file|
231
- if File.extname(content_file) == '.haml'
245
+ if type_from_path(content_file) == :haml
232
246
  props.eval(File.read(content_file, :encoding => 'UTF-8'), locale)
233
247
  else
234
- headers = []
235
- File.open(content_file, :encoding => 'UTF-8') do |f|
236
- while (line = f.gets) =~ /^(- |)@\w/
237
- if line !~ /^-/
238
- line = '- ' + line
239
- end
240
- headers << line
241
- end
248
+ headers, excerpt = parse_headers(content_file)
249
+ props.eval(headers, locale)
250
+ if !excerpt.empty?
251
+ props.set_prop(locale, "excerpt", excerpt)
242
252
  end
243
- props.eval(headers.join("\n"), locale)
253
+ props.set_prop(locale, "content_type", type_from_path(content_file))
244
254
  end
245
255
  cleanup_properties(props, locale)
246
256
  end
@@ -256,10 +266,123 @@ module Amber
256
266
  end
257
267
  end
258
268
 
269
+ #
270
+ # parses a content_file's property headers and tries to extract the
271
+ # first paragraph.
272
+ #
273
+ def parse_headers(content_file)
274
+ headers = []
275
+ para1 = []
276
+ para2 = []
277
+ file_type = type_from_path(content_file)
278
+
279
+ File.open(content_file, :encoding => 'UTF-8') do |f|
280
+ while (line = f.gets) =~ /^(- |)@\w/
281
+ if line !~ /^-/
282
+ line = '- ' + line
283
+ end
284
+ headers << line
285
+ end
286
+ # eat empty lines
287
+ while line = f.gets
288
+ break unless line =~ /^\s*$/
289
+ end
290
+ # grab first two paragraphs
291
+ para1 << line
292
+ while line = f.gets
293
+ break if line =~ /^\s*$/
294
+ para1 << line
295
+ end
296
+ while line = f.gets
297
+ break if line =~ /^\s*$/
298
+ para2 << line
299
+ end
300
+ end
301
+
302
+ headers = headers.join
303
+ para1 = para1.join
304
+ para2 = para2.join
305
+ excerpt = ""
306
+
307
+ # pick the first non-heading paragraph.
308
+ # this is stupid, and chokes on nested headings.
309
+ # but is also cheap and fast :)
310
+ if file_type == :textile
311
+ if para1 =~ /^h[1-5]\. /
312
+ excerpt = para2
313
+ else
314
+ excerpt = para1
315
+ end
316
+ elsif file_type == :markdown
317
+ if para1 =~ /^#+ / || para1 =~ /^(===+|---+)\s*$/m
318
+ excerpt = para2
319
+ else
320
+ excerpt = para1
321
+ end
322
+ end
323
+ return [headers, excerpt]
324
+ end
325
+
326
+ ##
327
+ ## VARIABLES
328
+ ## Variables are associated with a page, but unlike properties they are not
329
+ ## inheritable. Variables are defined in a separate file.
330
+ ##
331
+ def variable_files
332
+ if @simple_page
333
+ directory = File.dirname(@file_path)
334
+ regexp = SIMPLE_VAR_MATCH_RE.call(@name)
335
+ else
336
+ directory = @file_path
337
+ regexp = VAR_FILE_MATCH_RE
338
+ end
339
+ hsh = {}
340
+ Dir.foreach(directory) do |file|
341
+ if file && match = regexp.match(file)
342
+ locale = match['locale'] || I18n.default_locale
343
+ hsh[locale.to_sym] = File.join(directory, file)
344
+ end
345
+ end
346
+ hsh
347
+ end
348
+
349
+ def load_variables
350
+ vars = {}
351
+ variable_files.each do |locale, var_file|
352
+ begin
353
+ if var_file =~ /\.ya?ml$/
354
+ vars[locale] = YAML.load_file(var_file)
355
+ elsif var_file =~ /\.json$/
356
+ vars[locale] = JSON.parse(File.read(var_file))
357
+ end
358
+ rescue StandardError => exc
359
+ Amber.logger.error('ERROR: could not load file #{var_file}: ' + exc.to_s)
360
+ end
361
+ end
362
+ return vars
363
+ end
364
+
365
+ def type_from_path(path)
366
+ case File.extname(path)
367
+ when ".text", ".textile"
368
+ :textile
369
+ when ".md", ".markdown"
370
+ :markdown
371
+ when ".haml"
372
+ :haml
373
+ when ".html"
374
+ :html
375
+ when ".erb"
376
+ :erb
377
+ else
378
+ :unknown
379
+ end
380
+ end
381
+
259
382
  # RAILS
260
383
  #def is_haml_template?(locale)
261
384
  # content_file(locale) =~ /\.haml$/
262
- # #@suffix == '.haml' || File.exists?(self.absolute_template_path(locale) + '.haml')
385
+ # #@suffix == '.haml' || File.exist?(self.absolute_template_path(locale) + '.haml')
263
386
  #end
264
387
 
265
388
  end
@@ -39,11 +39,11 @@ module Amber
39
39
  #
40
40
  def get(property_name, inheritable_only=false)
41
41
  if inheritable_only || @this.nil?
42
- instance_variable_get("@#{property_name}")
42
+ safe_instance_get("@#{property_name}")
43
43
  else
44
44
  value = @this.get(property_name)
45
45
  if value.nil?
46
- value = instance_variable_get("@#{property_name}")
46
+ value = safe_instance_get("@#{property_name}")
47
47
  end
48
48
  value
49
49
  end
@@ -68,11 +68,22 @@ module Amber
68
68
  def to_s
69
69
  "<" + instance_variables.map{|v| "#{v}=#{instance_variable_get(v)}"}.join(', ') + ">"
70
70
  end
71
+
72
+ private
73
+
74
+ def safe_instance_get(prop_name)
75
+ if !instance_variable_defined?(prop_name)
76
+ instance_variable_set(prop_name, nil)
77
+ end
78
+ instance_variable_get(prop_name)
79
+ end
71
80
  end
72
81
 
73
82
  class ThisPropertySet < PropertySet
74
83
  def initialize
84
+ @this = nil
75
85
  end
76
86
  end
87
+
77
88
  end
78
89
  end
@@ -122,7 +122,7 @@ module Amber
122
122
  content_file = content_file(file_locale)
123
123
  next unless content_file
124
124
  dest = destination_file(dest_dir, file_locale)
125
- unless Dir.exists?(File.dirname(dest))
125
+ unless Dir.exist?(File.dirname(dest))
126
126
  FileUtils.mkdir_p(File.dirname(dest))
127
127
  end
128
128
  if options[:force] || !File.exist?(dest) || File.mtime(content_file) > File.mtime(dest)
@@ -0,0 +1,4 @@
1
+ @title = "Your site's title"
2
+ @default_locale = :en
3
+ @locales = [:en, :fr]
4
+ @short_paths = true
@@ -1,5 +1,5 @@
1
1
  module Amber
2
2
  unless defined?(Amber::VERSION)
3
- VERSION = '0.3.8'
3
+ VERSION = '0.3.11'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amber
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.3.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elijah Sparrow
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-12 00:00:00.000000000 Z
11
+ date: 2016-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -142,8 +142,9 @@ files:
142
142
  - lib/amber/page_array.rb
143
143
  - lib/amber/render/apache.rb
144
144
  - lib/amber/render/asset.rb
145
- - lib/amber/render/autolink.rb
146
- - lib/amber/render/bracketlink.rb
145
+ - lib/amber/render/filter/autolink.rb
146
+ - lib/amber/render/filter/bracketlink.rb
147
+ - lib/amber/render/filter/variables.rb
147
148
  - lib/amber/render/helpers/blog_helper.rb
148
149
  - lib/amber/render/helpers/date_helper.rb
149
150
  - lib/amber/render/helpers/haml_helper.rb
@@ -157,6 +158,7 @@ files:
157
158
  - lib/amber/server.rb
158
159
  - lib/amber/site.rb
159
160
  - lib/amber/site_configuration.rb
161
+ - lib/amber/site_loader.rb
160
162
  - lib/amber/static_page.rb
161
163
  - lib/amber/static_page/filesystem.rb
162
164
  - lib/amber/static_page/page_properties.rb
@@ -164,6 +166,7 @@ files:
164
166
  - lib/amber/static_page/render.rb
165
167
  - lib/amber/templates/apache_config.erb
166
168
  - lib/amber/templates/apache_config_with_prefix.erb
169
+ - lib/amber/templates/config.rb
167
170
  - lib/amber/templates/htaccess.erb
168
171
  - lib/amber/version.rb
169
172
  - locales/rails-i18n/af.yml
@@ -291,3 +294,4 @@ signing_key:
291
294
  specification_version: 4
292
295
  summary: Static website generator
293
296
  test_files: []
297
+ has_rdoc:
@@ -1,78 +0,0 @@
1
- # encoding: utf-8
2
- # adapted from https://github.com/tenderlove/rails_autolink
3
- # MIT license
4
-
5
- module Amber::Render::Autolink
6
-
7
- def self.auto_link(text)
8
- auto_link_email_addresses(auto_link_urls(text))
9
- end
10
-
11
- private
12
-
13
- AUTO_LINK_RE = %r{
14
- (?: ((?:ed2k|ftp|http|https|irc|mailto|news|gopher|nntp|telnet|webcal|xmpp|callto|feed|svn|urn|aim|rsync|tag|ssh|sftp|rtsp|afs|file):)// | www\. )
15
- [^\s<\u00A0]+
16
- }ix
17
-
18
- # regexps for determining context, used high-volume
19
- AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, /<a\b.*?>/i, /<\/a>/i]
20
-
21
- AUTO_EMAIL_LOCAL_RE = /[\w.!#\$%&'*\/=?^`{|}~+-]/
22
- AUTO_EMAIL_RE = /[\w.!#\$%+-]\.?#{AUTO_EMAIL_LOCAL_RE}*@[\w-]+(?:\.[\w-]+)+/
23
-
24
- BRACKETS = { ']' => '[', ')' => '(', '}' => '{' }
25
-
26
- WORD_PATTERN = RUBY_VERSION < '1.9' ? '\w' : '\p{Word}'
27
-
28
- # Turns all urls into clickable links. If a block is given, each url
29
- # is yielded and the result is used as the link text.
30
- def self.auto_link_urls(text)
31
- text.gsub(AUTO_LINK_RE) do
32
- scheme, href = $1, $&
33
- punctuation = []
34
-
35
- if auto_linked?($`, $')
36
- # do not change string; URL is already linked
37
- href
38
- else
39
- # don't include trailing punctuation character as part of the URL
40
- while href.sub!(/[^#{WORD_PATTERN}\/-]$/, '')
41
- punctuation.push $&
42
- if opening = BRACKETS[punctuation.last] and href.scan(opening).size > href.scan(punctuation.last).size
43
- href << punctuation.pop
44
- break
45
- end
46
- end
47
-
48
- #link_text = block_given?? yield(href) : href
49
- link_text = href.sub(/^#{scheme}\/\//,'')
50
- href = 'http://' + href unless scheme
51
- %(<a href="#{href}">#{link_text}</a>) + punctuation.reverse.join('')
52
- end
53
- end
54
- end
55
-
56
- # Turns all email addresses into clickable links.
57
- def self.auto_link_email_addresses(text)
58
- text.gsub(AUTO_EMAIL_RE) do
59
- text = $&
60
-
61
- if auto_linked?($`, $')
62
- text
63
- else
64
- #display_text = (block_given?) ? yield(text) : text
65
- #display_text = text
66
- text.gsub!('@', '&#064').gsub!('.', '&#046;')
67
- %(<a href="mailto:#{text}">#{text}</a>)
68
- end
69
- end
70
- end
71
-
72
- # Detects already linked context or position in the middle of a tag
73
- def self.auto_linked?(left, right)
74
- (left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or
75
- (left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3])
76
- end
77
-
78
- end
@@ -1,33 +0,0 @@
1
- # encoding: utf-8
2
-
3
- #
4
- # bracket links are links in the form [[label => target]] or [[page-name]]
5
- #
6
-
7
- module Amber::Render::Bracketlink
8
-
9
- # linking using double square brackets
10
- BRACKET_LINK_RE = /
11
- \[\[ # start [[
12
- ([^\[\]]+) # $text : one or more characters that are not [ or ] ($1)
13
- \]\] # end ]]
14
- /x
15
-
16
- def self.bracket_link(text, &block)
17
- text.gsub(BRACKET_LINK_RE) do |m|
18
- link_text = $~[1].strip
19
- if link_text =~ /^.+\s*[-=]>\s*.+$/
20
- # link_text == "from -> to"
21
- from, to = link_text.split(/\s*[-=]>\s*/)[0..1]
22
- from = "" unless from.instance_of? String # \ sanity check for
23
- to = "" unless from.instance_of? String # / badly formed links
24
- else
25
- # link_text == "to" (ie, no link label)
26
- from = nil
27
- to = link_text
28
- end
29
- yield(from, to)
30
- end
31
- end
32
-
33
- end