ruhoh 0.3.0 → 1.0.0.alpha

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 (71) hide show
  1. data/Gemfile +1 -0
  2. data/README.md +0 -33
  3. data/dash.html +1 -0
  4. data/history.json +29 -0
  5. data/lib/ruhoh/client/client.rb +30 -22
  6. data/lib/ruhoh/client/help.yml +6 -2
  7. data/lib/ruhoh/compiler.rb +8 -20
  8. data/lib/ruhoh/compilers/rss.rb +36 -0
  9. data/lib/ruhoh/compilers/theme.rb +41 -0
  10. data/lib/ruhoh/config.rb +49 -0
  11. data/lib/ruhoh/converters/converter.rb +17 -4
  12. data/lib/ruhoh/converters/markdown.rb +2 -2
  13. data/lib/ruhoh/db.rb +29 -2
  14. data/lib/ruhoh/deployers/s3.rb +1 -1
  15. data/lib/ruhoh/logger.rb +1 -1
  16. data/lib/ruhoh/page.rb +38 -22
  17. data/lib/ruhoh/parsers/javascripts.rb +67 -0
  18. data/lib/ruhoh/parsers/layouts.rb +6 -10
  19. data/lib/ruhoh/parsers/pages.rb +17 -24
  20. data/lib/ruhoh/parsers/partials.rb +6 -14
  21. data/lib/ruhoh/parsers/payload.rb +47 -0
  22. data/lib/ruhoh/parsers/posts.rb +27 -29
  23. data/lib/ruhoh/parsers/site.rb +2 -2
  24. data/lib/ruhoh/parsers/stylesheets.rb +75 -0
  25. data/lib/ruhoh/parsers/widgets.rb +104 -0
  26. data/lib/ruhoh/paths.rb +75 -0
  27. data/lib/ruhoh/previewer.rb +11 -5
  28. data/lib/ruhoh/program.rb +35 -4
  29. data/lib/ruhoh/templaters/asset_helpers.rb +66 -0
  30. data/lib/ruhoh/templaters/base_helpers.rb +143 -0
  31. data/lib/ruhoh/templaters/helpers.rb +1 -148
  32. data/lib/ruhoh/templaters/rmustache.rb +45 -4
  33. data/lib/ruhoh/urls.rb +46 -0
  34. data/lib/ruhoh/utils.rb +59 -17
  35. data/lib/ruhoh/version.rb +2 -2
  36. data/lib/ruhoh/watch.rb +26 -14
  37. data/lib/ruhoh.rb +38 -96
  38. data/ruhoh.gemspec +29 -10
  39. data/scaffolds/draft.html +9 -0
  40. data/scaffolds/page.html +0 -2
  41. data/scaffolds/post.html +0 -4
  42. data/scaffolds/theme/{images → javascripts}/.gitkeep +0 -0
  43. data/scaffolds/theme/layouts/default.html +2 -4
  44. data/scaffolds/theme/layouts/page.html +1 -1
  45. data/scaffolds/theme/layouts/post.html +1 -1
  46. data/scaffolds/theme/{css/style.css → media/.gitkeep} +0 -0
  47. data/scaffolds/theme/stylesheets/style.css +0 -0
  48. data/scaffolds/theme/theme.yml +27 -0
  49. data/scaffolds/theme/widgets/.gitkeep +0 -0
  50. data/spec/config_spec.rb +50 -0
  51. data/spec/db_spec.rb +28 -14
  52. data/spec/page_spec.rb +24 -35
  53. data/spec/parsers/layouts_spec.rb +5 -13
  54. data/spec/parsers/pages_spec.rb +13 -11
  55. data/spec/parsers/posts_spec.rb +34 -29
  56. data/spec/parsers/routes_spec.rb +2 -1
  57. data/spec/parsers/site_spec.rb +6 -5
  58. data/spec/setup_spec.rb +3 -47
  59. data/widgets/analytics/config.yml +5 -0
  60. data/{system_partials/analytics/getclicky → widgets/analytics/layouts/getclicky.html} +2 -2
  61. data/{system_partials/analytics/google → widgets/analytics/layouts/google.html} +1 -1
  62. data/widgets/comments/config.yml +12 -0
  63. data/{system_partials/comments/disqus → widgets/comments/layouts/disqus.html} +2 -2
  64. data/{system_partials/comments/facebook → widgets/comments/layouts/facebook.html} +1 -1
  65. data/{system_partials/comments/intensedebate → widgets/comments/layouts/intensedebate.html} +1 -1
  66. data/{system_partials/comments/livefyre → widgets/comments/layouts/livefyre.html} +1 -1
  67. data/widgets/google_prettify/config.yml +1 -0
  68. data/widgets/google_prettify/layouts/google_prettify.html +10 -0
  69. metadata +54 -31
  70. data/lib/ruhoh/templaters/base.rb +0 -57
  71. data/system_partials/syntax/google_prettify +0 -11
@@ -0,0 +1,67 @@
1
+ class Ruhoh
2
+ module Parsers
3
+ # Collect all the javascripts.
4
+ # Themes explicitly define which javascripts to load via theme.yml.
5
+ # Additionally, widgets may register javascript dependencies, which are resolved here.
6
+ module Javascripts
7
+
8
+ # Generates mappings to all registered javascripts.
9
+ # Returns Hash with layout names as keys and Array of asset Objects as values
10
+ def self.generate
11
+ theme_config = self.theme_config
12
+ assets = self.theme_javascripts(theme_config)
13
+ assets[Ruhoh.names.widgets] = self.widget_javascripts(theme_config)
14
+ assets
15
+ end
16
+
17
+ def self.theme_javascripts(theme_config)
18
+ return {} unless theme_config[Ruhoh.names.javascripts].is_a? Hash
19
+ assets = {}
20
+ theme_config[Ruhoh.names.javascripts].each do |key, value|
21
+ next if key == Ruhoh.names.widgets # Widgets are handled separately.
22
+ assets[key] = Array(value).map { |v|
23
+ {
24
+ "url" => "#{Ruhoh.urls.theme_javascripts}/#{v}",
25
+ "id" => File.join(Ruhoh.paths.theme_javascripts, v)
26
+ }
27
+ }
28
+ end
29
+
30
+ assets
31
+ end
32
+
33
+ # Notes:
34
+ # The automatic script inclusion is currently handled within the widget parser.
35
+ # This differs from the auto-stylesheet inclusion relative to themes,
36
+ # which is handled in the stylesheet parser.
37
+ # Make sure there are some standards with this.
38
+ def self.widget_javascripts(theme_config)
39
+ assets = []
40
+ Ruhoh::DB.widgets.each_value do |widget|
41
+ next unless widget[Ruhoh.names.javascripts]
42
+ assets += Array(widget[Ruhoh.names.javascripts]).map {|path|
43
+ {
44
+ "url" => [Ruhoh.urls.widgets, widget['name'], Ruhoh.names.javascripts, path].join('/'),
45
+ "id" => File.join(Ruhoh.paths.widgets, widget['name'], Ruhoh.names.javascripts, path)
46
+ }
47
+ }
48
+ end
49
+
50
+ assets
51
+ end
52
+
53
+ def self.theme_config
54
+ theme_config = Ruhoh::Utils.parse_yaml_file(Ruhoh.paths.theme_config_data)
55
+ if theme_config.nil?
56
+ Ruhoh::Friend.say{
57
+ yellow "WARNING: theme.yml config file not found:"
58
+ yellow " #{Ruhoh.paths.theme_config_data}"
59
+ }
60
+ return {}
61
+ end
62
+ return {} unless theme_config.is_a? Hash
63
+ theme_config
64
+ end
65
+ end #Javascripts
66
+ end #Parsers
67
+ end #Ruhoh
@@ -5,23 +5,20 @@ class Ruhoh
5
5
  # Generate layouts only from the active theme.
6
6
  def self.generate
7
7
  layouts = {}
8
- invalid = []
9
8
  self.files.each do |filename|
10
9
  id = File.basename(filename, File.extname(filename))
11
- layouts[id] = Ruhoh::Utils.parse_file(Ruhoh.paths.layouts, filename)
12
-
13
- if layouts[id].empty?
14
- error = "Invalid YAML Front Matter. Ensure this page has valid YAML, even if it's empty."
15
- invalid << [filename, error]
16
- end
10
+ data = Ruhoh::Utils.parse_layout_file(Ruhoh.paths.theme_layouts, filename)
11
+ data['id'] = id
12
+ layouts[id] = data
17
13
  end
18
14
 
19
- Ruhoh::Utils.report('Layouts', layouts, invalid)
15
+ Ruhoh::Utils.report('Layouts', layouts, [])
20
16
  layouts
21
17
  end
22
18
 
23
19
  def self.files
24
- FileUtils.cd(Ruhoh.paths.layouts) {
20
+ return [] unless FileTest.directory?(Ruhoh.paths.theme_layouts)
21
+ FileUtils.cd(Ruhoh.paths.theme_layouts) {
25
22
  return Dir["**/*.*"].select { |filename|
26
23
  next if FileTest.directory?(filename)
27
24
  next if ['_','.'].include? filename[0]
@@ -30,7 +27,6 @@ class Ruhoh
30
27
  }
31
28
  end
32
29
 
33
-
34
30
  end #Layouts
35
31
  end #Parsers
36
32
  end #Ruhoh
@@ -8,32 +8,30 @@ class Ruhoh
8
8
  Ruhoh.ensure_setup
9
9
 
10
10
  pages = self.files
11
- invalid = []
12
11
  dictionary = {}
13
12
 
14
13
  pages.each do |filename|
15
14
  id = self.make_id(filename)
16
15
  parsed_page = ''
17
- FileUtils.cd(Ruhoh.paths.site_source) { parsed_page = Ruhoh::Utils.parse_file(filename) }
18
- if parsed_page.empty?
19
- error = "Invalid Yaml Front Matter.\n Ensure this page has valid YAML, even if it's empty."
20
- invalid << [filename, error] ; next
21
- end
16
+ FileUtils.cd(Ruhoh.paths.base) { parsed_page = Ruhoh::Utils.parse_page_file(filename) }
22
17
 
23
18
  parsed_page['data']['id'] = id
24
19
  parsed_page['data']['url'] = self.permalink(parsed_page['data'])
25
20
  parsed_page['data']['title'] = parsed_page['data']['title'] || self.to_title(filename)
26
-
21
+ if parsed_page['data']['layout'].nil?
22
+ parsed_page['data']['layout'] = Ruhoh.config.pages_layout
23
+ end
24
+
27
25
  dictionary[id] = parsed_page['data']
28
26
  end
29
27
 
30
- Ruhoh::Utils.report('Pages', dictionary, invalid)
28
+ Ruhoh::Utils.report('Pages', dictionary, [])
31
29
  dictionary
32
30
  end
33
31
 
34
32
  def self.files
35
- FileUtils.cd(Ruhoh.paths.site_source) {
36
- return Dir["#{Ruhoh.folders.pages}/**/*.*"].select { |filename|
33
+ FileUtils.cd(Ruhoh.paths.base) {
34
+ return Dir["#{Ruhoh.names.pages}/**/*.*"].select { |filename|
37
35
  next unless self.is_valid_page?(filename)
38
36
  true
39
37
  }
@@ -43,40 +41,35 @@ class Ruhoh
43
41
  def self.is_valid_page?(filepath)
44
42
  return false if FileTest.directory?(filepath)
45
43
  return false if ['.'].include? filepath[0]
46
- Ruhoh.filters.pages.each {|regex| return false if filepath =~ regex }
44
+ Ruhoh.config.pages_exclude.each {|regex| return false if filepath =~ regex }
47
45
  true
48
46
  end
49
47
 
50
48
  def self.make_id(filename)
51
- filename.gsub(Regexp.new("^#{Ruhoh.folders.pages}/"), '')
49
+ filename.gsub(Regexp.new("^#{Ruhoh.names.pages}/"), '')
52
50
  end
53
51
 
54
52
  def self.to_title(filename)
55
53
  name = File.basename( filename, File.extname(filename) )
56
54
  name = filename.split('/')[-2] if name == 'index' && !filename.index('/').nil?
57
- name.gsub(/[\W\_]/, ' ').gsub(/\b\w/){$&.upcase}
55
+ name.gsub(/[^\p{Word}+]/u, ' ').gsub(/\b\w/){$&.upcase}
58
56
  end
59
57
 
60
58
  # Build the permalink for the given page.
61
- # Only recognize 'convertable' extensions for Markdown at the moment.
59
+ # Only recognize extensions registered from a 'convertable' module.
62
60
  # This means 'non-convertable' extensions should pass-through.
63
61
  #
64
62
  # Returns [String] the permalink for this page.
65
63
  def self.permalink(page)
66
- url = '/'
67
64
  ext = File.extname(page['id'])
68
- url += if ['.md', '.markdown'].include?(ext)
69
- page['id'].gsub(Regexp.new("#{ext}$"), '.html')
70
- else
71
- page['id']
72
- end
73
-
74
- url = url.split('/').reject{ |part| part[0] == '.' }.join('/')
75
- url = url.gsub(/\/index.html$/, '')
65
+ name = page['id'].gsub(Regexp.new("#{ext}$"), '')
66
+ ext = '.html' if Ruhoh::Converter.extensions.include?(ext)
67
+ url = name.split('/').map {|p| Ruhoh::Urls.to_url_slug(p) }.join('/')
68
+ url = "/#{url}#{ext}".gsub(/\/index.html$/, '')
76
69
  if page['permalink'] == 'pretty' || Ruhoh.config.pages_permalink == 'pretty'
77
70
  url = url.gsub(/\.html$/, '')
78
71
  end
79
- url = "/" if url.empty?
72
+ url = '/' if url.empty?
80
73
 
81
74
  url
82
75
  end
@@ -1,25 +1,19 @@
1
1
  class Ruhoh
2
-
3
2
  module Parsers
4
-
5
3
  module Partials
6
4
 
7
5
  def self.generate
8
- self.system_partials.merge(self.global_partials).merge(self.theme_partials)
6
+ self.global_partials.merge(self.theme_partials)
9
7
  end
10
-
8
+
11
9
  def self.theme_partials
12
- self.process(Ruhoh.paths.partials)
10
+ self.process(Ruhoh.paths.theme_partials)
13
11
  end
14
12
 
15
13
  def self.global_partials
16
- self.process(Ruhoh.paths.global_partials)
17
- end
18
-
19
- def self.system_partials
20
- self.process(File.join(Ruhoh::Root, 'system_partials'))
14
+ self.process(Ruhoh.paths.partials)
21
15
  end
22
-
16
+
23
17
  def self.process(path)
24
18
  return {} unless File.exist?(path)
25
19
 
@@ -28,14 +22,12 @@ class Ruhoh
28
22
  Dir.glob("**/*").each { |filename|
29
23
  next if FileTest.directory?(filename)
30
24
  next if ['.'].include? filename[0]
31
- File.open(filename) { |f| partials[filename] = f.read }
25
+ File.open(filename, 'r:UTF-8') { |f| partials[filename] = f.read }
32
26
  }
33
27
  }
34
28
  partials
35
29
  end
36
30
 
37
31
  end #Partials
38
-
39
32
  end #Parsers
40
-
41
33
  end #Ruhoh
@@ -0,0 +1,47 @@
1
+ class Ruhoh
2
+ module Parsers
3
+ module Payload
4
+
5
+ def self.generate
6
+ {
7
+ "db" => {
8
+ "pages" => Ruhoh::DB.pages,
9
+ "posts" => self.determine_category_and_tag_urls,
10
+ },
11
+ "site" => Ruhoh::DB.site,
12
+ 'page' => {},
13
+ "urls" => {
14
+ "theme_stylesheets" => Ruhoh.urls.theme_stylesheets,
15
+ "theme_javascripts" => Ruhoh.urls.theme_javascripts,
16
+ "theme_media" => Ruhoh.urls.theme_media,
17
+ "media" => Ruhoh.urls.media,
18
+ }
19
+ }
20
+ end
21
+
22
+ # This is an ugly hack to determine the proper category and tag urls.
23
+ # TODO: Refactor this out.
24
+ def self.determine_category_and_tag_urls
25
+ return nil unless Ruhoh::DB.routes && Ruhoh::DB.posts
26
+ categories_url = nil
27
+ ['/categories', '/categories.html'].each { |url|
28
+ categories_url = url and break if Ruhoh::DB.routes.key?(url)
29
+ }
30
+ Ruhoh::DB.posts['categories'].each do |key, value|
31
+ Ruhoh::DB.posts['categories'][key]['url'] = "#{categories_url}##{value['name']}-ref"
32
+ end
33
+
34
+ tags_url = nil
35
+ ['/tags', '/tags.html'].each { |url|
36
+ tags_url = url and break if Ruhoh::DB.routes.key?(url)
37
+ }
38
+ Ruhoh::DB.posts['tags'].each do |key, value|
39
+ Ruhoh::DB.posts['tags'][key]['url'] = "#{tags_url}##{value['name']}-ref"
40
+ end
41
+
42
+ Ruhoh::DB.posts
43
+ end
44
+
45
+ end #Payload
46
+ end #Parsers
47
+ end #Ruhoh
@@ -30,14 +30,10 @@ class Ruhoh
30
30
 
31
31
  self.files.each do |filename|
32
32
  parsed_page = ''
33
- FileUtils.cd(Ruhoh.paths.site_source) { parsed_page = Ruhoh::Utils.parse_file(filename) }
34
- if parsed_page.empty?
35
- error = "Invalid YAML Front Matter. Ensure this page has valid YAML, even if it's empty."
36
- invalid << [filename, error] ; next
37
- end
33
+ FileUtils.cd(Ruhoh.paths.base) { parsed_page = Ruhoh::Utils.parse_page_file(filename) }
38
34
  data = parsed_page['data']
39
35
 
40
- filename_data = self.parse_filename(filename)
36
+ filename_data = self.parse_page_filename(filename)
41
37
  if filename_data.empty?
42
38
  error = "Invalid Filename Format. Format should be: my-post-title.ext"
43
39
  invalid << [filename, error] ; next
@@ -59,6 +55,7 @@ class Ruhoh
59
55
  data['id'] = filename
60
56
  data['title'] = data['title'] || filename_data['title']
61
57
  data['url'] = self.permalink(data)
58
+ data['layout'] = Ruhoh.config.posts_layout if data['layout'].nil?
62
59
  dictionary[filename] = data
63
60
  end
64
61
 
@@ -77,8 +74,8 @@ class Ruhoh
77
74
  end
78
75
 
79
76
  def self.files
80
- FileUtils.cd(Ruhoh.paths.site_source) {
81
- return Dir["#{Ruhoh.folders.posts}/**/*.*"].select { |filename|
77
+ FileUtils.cd(Ruhoh.paths.base) {
78
+ return Dir["#{Ruhoh.names.posts}/**/*.*"].select { |filename|
82
79
  next unless self.is_valid_page?(filename)
83
80
  true
84
81
  }
@@ -88,7 +85,7 @@ class Ruhoh
88
85
  def self.is_valid_page?(filepath)
89
86
  return false if FileTest.directory?(filepath)
90
87
  return false if ['.'].include? filepath[0]
91
- Ruhoh.filters.posts.each {|regex| return false if filepath =~ regex }
88
+ Ruhoh.config.posts_exclude.each {|regex| return false if filepath =~ regex }
92
89
  true
93
90
  end
94
91
 
@@ -102,7 +99,7 @@ class Ruhoh
102
99
  ordered_posts
103
100
  end
104
101
 
105
- def self.parse_filename(filename)
102
+ def self.parse_page_filename(filename)
106
103
  data = *filename.match(DateMatcher)
107
104
  data = *filename.match(Matcher) if data.empty?
108
105
  return {} if data.empty?
@@ -127,41 +124,44 @@ class Ruhoh
127
124
 
128
125
  # my-post-title ===> My Post Title
129
126
  def self.to_title(file_slug)
130
- file_slug.gsub(/[\W\_]/, ' ').gsub(/\b\w/){$&.upcase}
127
+ file_slug.gsub(/[^\p{Word}+]/u, ' ').gsub(/\b\w/){$&.upcase}
131
128
  end
132
129
 
133
- # My Post Title ===> my-post-title
134
- def self.to_slug(title)
135
- title.downcase.strip.gsub(/\s/, '-').gsub(/[^\w-]/, '')
136
- end
137
-
138
130
  # Used in the client implementation to turn a draft into a post.
139
131
  def self.to_filename(data)
140
- File.join(Ruhoh.paths.posts, "#{self.to_slug(data['title'])}.#{data['ext']}")
132
+ File.join(Ruhoh.paths.posts, "#{Ruhoh::Urls.to_slug(data['title'])}.#{data['ext']}")
141
133
  end
142
134
 
143
135
  # Another blatently stolen method from Jekyll
136
+ # The category is only the first one if multiple categories exist.
144
137
  def self.permalink(post)
145
138
  date = Date.parse(post['date'])
146
- title = post['title'].downcase.gsub(' ', '-').gsub('.','')
147
- format = post['permalink'] || Ruhoh.config.permalink || "/:categories/:year/:month/:day/:title.html"
139
+ title = Ruhoh::Urls.to_url_slug(post['title'])
140
+ format = post['permalink'] || Ruhoh.config.posts_permalink || "/:categories/:year/:month/:day/:title.html"
141
+
142
+ # Use the literal permalink if it is a non-tokenized string.
143
+ unless format.include?(':')
144
+ url = format.gsub(/^\//, '').split('/').map {|p| CGI::escape(p) }.join('/')
145
+ return "/#{url}"
146
+ end
147
+
148
+ filename = File.basename(post['id'], File.extname(post['id']))
149
+ category = Array(post['categories'])[0]
150
+ category = category.split('/').map {|c| Ruhoh::Urls.to_url_slug(c) }.join('/') if category
148
151
 
149
152
  url = {
150
153
  "year" => date.strftime("%Y"),
151
154
  "month" => date.strftime("%m"),
152
155
  "day" => date.strftime("%d"),
153
- "title" => CGI::escape(title),
156
+ "title" => title,
157
+ "filename" => filename,
154
158
  "i_day" => date.strftime("%d").to_i.to_s,
155
159
  "i_month" => date.strftime("%m").to_i.to_s,
156
- "categories" => Array(post['categories'])[0] || '',
157
- "output_ext" => 'html' # what's this for?
160
+ "categories" => category || '',
158
161
  }.inject(format) { |result, token|
159
162
  result.gsub(/:#{Regexp.escape token.first}/, token.last)
160
- }.gsub(/\/\//, "/")
163
+ }.gsub(/\/+/, "/")
161
164
 
162
- # sanitize url
163
- url = url.split('/').reject{ |part| part =~ /^\.+$/ }.join('/')
164
- url += "/" if url =~ /\/$/
165
165
  url
166
166
  end
167
167
 
@@ -223,7 +223,6 @@ class Ruhoh
223
223
  tags[tag] = {
224
224
  'count' => 1,
225
225
  'name' => tag,
226
- 'url' => "/tags.html##{tag}-ref",
227
226
  'posts' => []
228
227
  }
229
228
  end
@@ -236,7 +235,7 @@ class Ruhoh
236
235
 
237
236
  def self.parse_categories(ordered_posts)
238
237
  categories = {}
239
-
238
+
240
239
  ordered_posts.each do |post|
241
240
  Array(post['categories']).each do |cat|
242
241
  cat = Array(cat).join('/')
@@ -246,7 +245,6 @@ class Ruhoh
246
245
  categories[cat] = {
247
246
  'count' => 1,
248
247
  'name' => cat,
249
- 'url' => "/categories.html##{cat}-ref",
250
248
  'posts' => []
251
249
  }
252
250
  end
@@ -6,8 +6,8 @@ class Ruhoh
6
6
  module Site
7
7
 
8
8
  def self.generate
9
- site = Ruhoh::Utils.parse_file_as_yaml(Ruhoh.paths.site_source, Ruhoh.files.site) || {}
10
- config = Ruhoh::Utils.parse_file_as_yaml(Ruhoh.paths.site_source, Ruhoh.files.config)
9
+ site = Ruhoh::Utils.parse_yaml_file(Ruhoh.paths.site_data) || {}
10
+ config = Ruhoh::Utils.parse_yaml_file(Ruhoh.paths.config_data)
11
11
  site['config'] = config
12
12
  site
13
13
  end
@@ -0,0 +1,75 @@
1
+ class Ruhoh
2
+ module Parsers
3
+ # Collect all registered stylesheets.
4
+ # Themes explicitly define which stylesheets to load via theme.yml.
5
+ # All styling is managed by the theme, including widget styles.
6
+ # If the theme provides widget stylesheets they will load automatically.
7
+ # theme.yml may also specify an explicit widget stylesheet to load.
8
+ module Stylesheets
9
+
10
+ # Generates mappings to all registered stylesheets.
11
+ # Returns Hash with layout names as keys and Array of asset Objects as values
12
+ def self.generate
13
+ theme_config = self.theme_config
14
+ assets = self.theme_stylesheets(theme_config)
15
+ assets[Ruhoh.names.widgets] = self.widget_stylesheets(theme_config)
16
+ assets
17
+ end
18
+
19
+ # Create mappings for stylesheets registered to the theme layouts.
20
+ # Themes register stylesheets relative to their layouts.
21
+ # Returns Hash with layout names as keys and Array of asset Objects as values.
22
+ def self.theme_stylesheets(theme_config)
23
+ return {} unless theme_config[Ruhoh.names.stylesheets].is_a? Hash
24
+ assets = {}
25
+ theme_config[Ruhoh.names.stylesheets].each do |key, value|
26
+ next if key == Ruhoh.names.widgets # Widgets are handled separately.
27
+ assets[key] = Array(value).map { |v|
28
+ {
29
+ "url" => "#{Ruhoh.urls.theme_stylesheets}/#{v}",
30
+ "id" => File.join(Ruhoh.paths.theme_stylesheets, v)
31
+ }
32
+ }
33
+ end
34
+
35
+ assets
36
+ end
37
+
38
+ # Create mappings for stylesheets registered to a given widget.
39
+ # A theme may provide widget stylesheets which will load automatically,
40
+ # provided they adhere to the default naming rules.
41
+ # Themes may also specify an explicit widget stylesheet to load.
42
+ #
43
+ # Returns Array of asset objects.
44
+ def self.widget_stylesheets(theme_config)
45
+ assets = []
46
+ Ruhoh::DB.widgets.each_key do |name|
47
+ default_name = "#{name}.css"
48
+ stylesheet = theme_config[Ruhoh.names.stylesheets][Ruhoh.names.widgets][name] rescue default_name
49
+ stylesheet ||= default_name
50
+ file = File.join(Ruhoh.paths.theme_widgets, name, Ruhoh.names.stylesheets, stylesheet)
51
+ next unless File.exists?(file)
52
+ assets << {
53
+ "url" => [Ruhoh.urls.theme_widgets, name, Ruhoh.names.stylesheets, stylesheet].join('/'),
54
+ "id" => file
55
+ }
56
+ end
57
+
58
+ assets
59
+ end
60
+
61
+ def self.theme_config
62
+ theme_config = Ruhoh::Utils.parse_yaml_file(Ruhoh.paths.theme_config_data)
63
+ if theme_config.nil?
64
+ Ruhoh::Friend.say{
65
+ yellow "WARNING: theme.yml config file not found:"
66
+ yellow " #{Ruhoh.paths.theme_config_data}"
67
+ }
68
+ return {}
69
+ end
70
+ return {} unless theme_config.is_a? Hash
71
+ theme_config
72
+ end
73
+ end #Stylesheets
74
+ end #Parsers
75
+ end #Ruhoh
@@ -0,0 +1,104 @@
1
+ class Ruhoh
2
+ module Parsers
3
+ module Widgets
4
+ WidgetStructure = Struct.new(
5
+ :name,
6
+ :config,
7
+ :javascripts,
8
+ :layout
9
+ )
10
+
11
+ # Process available widgets into widget dictionary.
12
+ #
13
+ # Returns Dictionary of widget data.
14
+ def self.generate
15
+ widgets = {}
16
+ self.widgets.each do |name|
17
+ config = self.process_config(name)
18
+ widgets[name] = WidgetStructure.new(
19
+ name,
20
+ config,
21
+ self.process_javascripts(config, name),
22
+ self.process_layout(config, name)
23
+ )
24
+ end
25
+ Ruhoh::Utils.report('Widgets', widgets, [])
26
+
27
+ widgets
28
+ end
29
+
30
+ # Find the widgets.
31
+ #
32
+ # Returns Array of widget names.
33
+ def self.widgets
34
+ names = []
35
+ if FileTest.directory?(Ruhoh.paths.widgets)
36
+ FileUtils.cd(Ruhoh.paths.widgets) { names += Dir["*"] }
37
+ end
38
+ if FileTest.directory?(Ruhoh.paths.system_widgets)
39
+ FileUtils.cd(Ruhoh.paths.system_widgets) { names += Dir["*"] }
40
+ end
41
+ names.uniq!
42
+ names
43
+ end
44
+
45
+ # Process the widget configuration params.
46
+ #
47
+ # Returns Hash of configuration params.
48
+ def self.process_config(widget_name)
49
+ system_config = Ruhoh::Utils.parse_yaml_file(Ruhoh.paths.system_widgets, widget_name, Ruhoh.names.config_data) || {}
50
+ user_config = Ruhoh::Utils.parse_yaml_file(Ruhoh.paths.widgets, widget_name, Ruhoh.names.config_data) || {}
51
+ config = Ruhoh::Utils.deep_merge(system_config, user_config)
52
+ config['layout'] ||= widget_name
53
+ config['stylesheet'] ||= widget_name
54
+ config
55
+ end
56
+
57
+ # Process widget script dependencies.
58
+ # Script dependencies may be set in the config.
59
+ # Look for default script at: scripts/{widget_name}.js if no config.
60
+ # If found, we include it, else no javascripts will load.
61
+ #
62
+ # Returns Array of script filenames to load.
63
+ def self.process_javascripts(config, widget_name)
64
+ scripts = config[Ruhoh.names.javascripts] ? Array(config[Ruhoh.names.javascripts]) : []
65
+
66
+ # Try for the default script if no config.
67
+ if scripts.empty?
68
+ script_file = File.join(Ruhoh.paths.widgets, widget_name, Ruhoh.names.javascripts, "#{widget_name}.js")
69
+ if File.exist?(script_file)
70
+ scripts << "#{widget_name}.js"
71
+ else
72
+ script_file = File.join(Ruhoh.paths.system_widgets, widget_name, Ruhoh.names.javascripts, "#{widget_name}.js")
73
+ scripts << "#{widget_name}.js" if File.exist?(script_file)
74
+ end
75
+ end
76
+
77
+ scripts
78
+ end
79
+
80
+ # Determine and process the correct widget layout.
81
+ # The layout may be manually configured by the user,
82
+ # else system defaults will be used.
83
+ # Layouts cascade from: theme -> blog -> system
84
+ #
85
+ # Returns String of rendered layout content.
86
+ def self.process_layout(config, widget_name)
87
+ layout = nil
88
+ layout_path = File.join(widget_name, 'layouts', "#{config['layout']}.html")
89
+ [
90
+ File.join(Ruhoh.paths.theme_widgets, layout_path),
91
+ File.join(Ruhoh.paths.widgets, layout_path),
92
+ File.join(Ruhoh.paths.system_widgets, layout_path)
93
+ ].each do |path|
94
+ layout = path and break if File.exist?(path)
95
+ end
96
+
97
+ return '' unless layout
98
+ content = File.open(layout, 'r:UTF-8') { |f| f.read }
99
+ Mustache.render(content, {'config' => config})
100
+ end
101
+
102
+ end #Widgets
103
+ end #Parsers
104
+ end #Ruhoh