ruhoh 2.3 → 2.4

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.
data/Gemfile CHANGED
@@ -3,7 +3,6 @@ gemspec
3
3
 
4
4
  gem 'rack', "~> 1.4"
5
5
  gem 'directory_watcher', "~> 1.4.0"
6
- #gem 'psych', "~> 1.3", :platforms => [:ruby_18, :mingw_18]
7
6
  gem 'redcarpet', "~> 2.1"
8
7
  gem 'nokogiri', "~> 1.5"
9
8
 
@@ -0,0 +1,26 @@
1
+ Feature: base_path
2
+ As a content publisher
3
+ I want to configure base_path
4
+ so I can publish content relative to arbitrary webhost file structure requirements.
5
+
6
+ Scenario: Setting base_path
7
+ Given some files with values:
8
+ | file | body |
9
+ | config.yml | base_path: '/hello/world' |
10
+ | _root/index.html | |
11
+ | essays/water.md | <span>{{ page.url }}</span> |
12
+ When I compile my site
13
+ Then my compiled site should have the file "hello/world/index.html"
14
+ And my compiled site should have the file "hello/world/essays/water/index.html"
15
+ And this file should contain the content node "span|/hello/world/essays/water"
16
+
17
+ Scenario: Setting base_path with compile_as_root
18
+ Given some files with values:
19
+ | file | body |
20
+ | config.yml | base_path: '/hello/world' \ncompile_as_root: true |
21
+ | _root/index.html | |
22
+ | essays/water.md | <span>{{ page.url }}</span> |
23
+ When I compile my site
24
+ Then my compiled site should have the file "index.html"
25
+ And my compiled site should have the file "essays/water/index.html"
26
+ And this file should contain the content node "span|/hello/world/essays/water"
@@ -30,3 +30,34 @@ Feature: Data
30
30
  And this file should contain the content node "city|alhambra"
31
31
  And this file should contain the content node "li|mango"
32
32
  And this file should contain the content node "li|kiwi"
33
+
34
+ Scenario: Defining a basic data structure in data.json
35
+ Given the file "data.json" with body:
36
+ """
37
+ {
38
+ "address": {
39
+ "city": "alhambra"
40
+ },
41
+ "name": "jade",
42
+ "fruits": [
43
+ "mango",
44
+ "kiwi"
45
+ ]
46
+ }
47
+ """
48
+ And the file "_root/index.html" with body:
49
+ """
50
+ <name>{{ data.name }}</name>
51
+ <city>{{ data.address.city }}</city>
52
+ <ul>
53
+ {{# data.fruits }}
54
+ <li>{{ . }}</li>
55
+ {{/ data.fruits }}
56
+ </ul>
57
+ """
58
+ When I compile my site
59
+ Then my compiled site should have the file "index.html"
60
+ And this file should contain the content node "name|jade"
61
+ And this file should contain the content node "city|alhambra"
62
+ And this file should contain the content node "li|mango"
63
+ And this file should contain the content node "li|kiwi"
@@ -0,0 +1,33 @@
1
+ Feature: Config
2
+ As a content publisher
3
+ I want the option to configure my site in JSON
4
+ because JSON is the preferred data format for modern web-technologies
5
+ and it benefits me to learn and get used to it, not to mention YAML is more
6
+ problematic than it's worth.
7
+
8
+ Scenario: Setting configuration in JSON
9
+ Given some files with values:
10
+ | file | body |
11
+ | config.json | { "production_url" : "http://hello-world.com" } |
12
+ | _root/index.html | <span>{{ urls.production_url }}</span> |
13
+ When I compile my site
14
+ Then my compiled site should have the file "index.html"
15
+ And this file should contain the content node "span|http://hello-world.com"
16
+
17
+ Scenario: Setting Top Metadata in JSON
18
+ Given the file "_root/index.html" with body:
19
+ """
20
+ {
21
+ "title" : "Hello World",
22
+ "author" : "Isosceles",
23
+ "tags" : ["apple", "orange"]
24
+ }
25
+
26
+ <title>{{ page.title }}</title>
27
+
28
+ <author>{{ page.author }}</author>
29
+ """
30
+ When I compile my site
31
+ Then my compiled site should have the file "index.html"
32
+ And this file should contain the content node "title|Hello World"
33
+ And this file should contain the content node "author|Isosceles"
@@ -39,3 +39,18 @@ Feature: Layouts
39
39
  Then my compiled site should have the file "essays/hello/index.html"
40
40
  And this file should contain the content node "div#minimal-layout|cookie dough"
41
41
  And this file should contain the content node "div#minimal-sub-layout|cookie dough"
42
+
43
+ Scenario: Defining more than two layouts.
44
+ Given some files with values:
45
+ | file | body | layout |
46
+ | layouts/outer.md | <div id="minimal-outer-layout">outer {{{ content }}}</div> | |
47
+ | layouts/inner.md | <div id="minimal-inner-layout">inner {{{ content }}}</div> | outer |
48
+ | layouts/essays.md | <div id="minimal-layout">{{{ content }}}</div> | inner |
49
+ | essays/hello.md | cookie dough | essays |
50
+ When I compile my site
51
+ Then my compiled site should have the file "essays/hello/index.html"
52
+ And this file should contain the content node "div#minimal-outer-layout|inner"
53
+ And this file should contain the content node "div#minimal-inner-layout|cookie dough"
54
+ And this file should NOT contain the content node "div#minimal-inner-layout|outer"
55
+ And this file should NOT contain the content node "div#minimal-layout|outer"
56
+ And this file should NOT contain the content node "div#minimal-layout|inner"
@@ -40,11 +40,17 @@ def make_file(opts)
40
40
  metadata = data.empty? ? '' : data.to_yaml.to_s + "\n---\n"
41
41
 
42
42
  File.open(path, "w+") { |file|
43
- file.puts <<-TEXT
43
+ if metadata.empty?
44
+ file.puts <<-TEXT
45
+ #{ opts[:body] }
46
+ TEXT
47
+ else
48
+ file.puts <<-TEXT
44
49
  #{ metadata }
45
50
 
46
51
  #{ opts[:body] }
47
52
  TEXT
53
+ end
48
54
  }
49
55
  end
50
56
 
@@ -1,4 +1,26 @@
1
1
  [
2
+ {
3
+ "version" : "2.4",
4
+ "date" : "10.09.2013",
5
+ "changes" : [
6
+ "(Internal) Add Ruhoh::Parse service to parse various file formats.",
7
+ "Remove psych gem dependency for yaml parsing. Native yaml lib should still work for ruby 1.9.2+"
8
+ ],
9
+ "features" : [
10
+ "@stebalien exposes total_pages in pagination view.",
11
+ "@stebalien adds spport for unlimited nested sub-layouts.",
12
+ "Support JSON data-type in config, data, and page metadata.",
13
+ "Add 'compile_as_root' configuration setting to automated hosting on sub-directory style web-hosts."
14
+ ],
15
+ "bugs" : [
16
+ "Don't choke on 'theme' check if them config not formatted correctly.",
17
+ "Ensure base_path is well-formed with leading and trailing slash.",
18
+ "Expose urls.production and urls.production_url in the view.",
19
+ "data.yml/data.json should be discoverable on every cascade level.",
20
+ "Allow collections to be discoverable from all cascade levels."
21
+ ]
22
+ }
23
+ ,
2
24
  {
3
25
  "version" : "2.3",
4
26
  "date" : "25.08.2013",
@@ -1,8 +1,5 @@
1
1
  # encoding: UTF-8
2
2
  Encoding.default_internal = 'UTF-8'
3
- require 'yaml'
4
- require 'psych'
5
- YAML::ENGINE.yamler = 'psych'
6
3
  require 'json'
7
4
  require 'time'
8
5
  require 'cgi'
@@ -11,12 +8,14 @@ require 'ostruct'
11
8
  require 'delegate'
12
9
  require 'digest'
13
10
  require 'observer'
11
+ require 'set'
14
12
 
15
13
  require 'mustache'
16
14
 
17
15
  require 'ruhoh/logger'
18
16
  require 'ruhoh/utils'
19
17
  require 'ruhoh/friend'
18
+ require 'ruhoh/parse'
20
19
 
21
20
  require 'ruhoh/converter'
22
21
  require 'ruhoh/views/master_view'
@@ -65,7 +64,7 @@ class Ruhoh
65
64
  def config(reload=false)
66
65
  return @config unless (reload or @config.nil?)
67
66
 
68
- config = Ruhoh::Utils.parse_yaml_file(@base, "config.yml") || {}
67
+ config = Ruhoh::Parse.data_file(@base, "config") || {}
69
68
  config['compiled'] = config['compiled'] ? File.expand_path(config['compiled']) : "compiled"
70
69
 
71
70
  config['_root'] ||= {}
@@ -92,7 +91,7 @@ class Ruhoh
92
91
  @paths.system = File.join(Ruhoh::Root, "system")
93
92
  @paths.compiled = @config["compiled"]
94
93
 
95
- theme = @config.find{ |resource, data| data['use'] == "theme" }
94
+ theme = @config.find { |resource, data| data.is_a?(Hash) && data['use'] == "theme" }
96
95
  if theme
97
96
  Ruhoh::Friend.say { plain "Using theme: \"#{theme[0]}\""}
98
97
  @paths.theme = File.join(@base, theme[0])
@@ -140,9 +139,20 @@ class Ruhoh
140
139
  end
141
140
 
142
141
  def base_path
143
- (env == 'production') ?
144
- config['base_path'] :
145
- '/'
142
+ return '/' unless (env == 'production')
143
+
144
+ string = config['base_path'].chomp('/').reverse.chomp('/').reverse
145
+ return '/' if string.empty? || string == '/'
146
+ "/#{ string }/"
147
+ end
148
+
149
+ def compiled_path(url)
150
+ if config['compile_as_root']
151
+ url = url.gsub(/^#{ base_path.chomp('/') }\/?/, '')
152
+ end
153
+
154
+ path = File.expand_path(File.join(paths.compiled, url)).gsub(/\/{2,}/, '/')
155
+ CGI.unescape(path)
146
156
  end
147
157
 
148
158
  # @config['base_path'] is assumed to be well-formed.
@@ -81,7 +81,7 @@ module Ruhoh::Base
81
81
  def config
82
82
  config = @ruhoh.config[resource_name] || {}
83
83
  unless config.is_a?(Hash)
84
- Ruhoh.log.error("'#{resource_name}' config key in config.yml" +
84
+ Ruhoh.log.error("'#{resource_name}' config key in config" +
85
85
  " is a #{config.class}; it needs to be a Hash (object).")
86
86
  end
87
87
  config
@@ -237,6 +237,10 @@ module Ruhoh::Base
237
237
  File.open(pointer['realpath'], 'r:UTF-8') { |f| f.read }
238
238
  end
239
239
 
240
+ def compiled_path
241
+ @compiled_path ||= @ruhoh.compiled_path(@ruhoh.to_url(url_endpoint))
242
+ end
243
+
240
244
  protected
241
245
 
242
246
  # Load the registered resource else default to Pages if not configured.
@@ -251,7 +255,7 @@ module Ruhoh::Base
251
255
  else
252
256
  klass = camelize(type)
253
257
  Friend.say {
254
- red "#{resource_name} resource set to use:'#{type}' in config.yml" +
258
+ red "#{resource_name} resource set to use:'#{type}' in config" +
255
259
  " but Ruhoh::Resources::#{klass} does not exist."
256
260
  }
257
261
  abort
@@ -9,6 +9,26 @@ module Ruhoh::Base
9
9
  @ruhoh = collection.ruhoh
10
10
  @collection = collection
11
11
  end
12
+
13
+ def setup_compilable
14
+ return false unless collection_exists?
15
+
16
+ compile_collection_path
17
+ end
18
+
19
+ def compile_collection_path
20
+ FileUtils.mkdir_p(@collection.compiled_path)
21
+ end
22
+
23
+ def collection_exists?
24
+ collection = @collection
25
+ unless @collection.paths?
26
+ Ruhoh::Friend.say { yellow "#{ collection.resource_name.capitalize }: directory not found - skipping." }
27
+ return false
28
+ end
29
+ Ruhoh::Friend.say { cyan "#{ collection.resource_name.capitalize }: (copying valid files)" }
30
+ true
31
+ end
12
32
  end
13
33
 
14
34
  module CompilableAsset
@@ -22,24 +42,15 @@ module Ruhoh::Base
22
42
  #
23
43
  # @returns Nothing.
24
44
  def run
25
- collection = @collection
26
-
27
- unless @collection.paths?
28
- Ruhoh::Friend.say { yellow "#{collection.resource_name.capitalize}: directory not found - skipping." }
29
- return
30
- end
31
- Ruhoh::Friend.say { cyan "#{collection.resource_name.capitalize}: (copying valid files)" }
45
+ return unless setup_compilable
32
46
 
33
- compiled_path = Ruhoh::Utils.url_to_path(@ruhoh.to_url(@collection.url_endpoint), @ruhoh.paths.compiled)
34
- FileUtils.mkdir_p compiled_path
35
-
36
47
  manifest = {}
37
48
  @collection.files.values.each do |pointer|
38
49
  digest = Digest::MD5.file(pointer['realpath']).hexdigest
39
50
  digest_file = pointer['id'].sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
40
51
  manifest[pointer['id']] = digest_file
41
52
 
42
- compiled_file = File.join(compiled_path, digest_file)
53
+ compiled_file = File.join(@collection.compiled_path, digest_file)
43
54
  FileUtils.mkdir_p File.dirname(compiled_file)
44
55
  FileUtils.cp_r pointer['realpath'], compiled_file
45
56
  Ruhoh::Friend.say { green " > #{pointer['id']}" }
@@ -47,7 +47,6 @@ module Ruhoh::Base
47
47
  module PageLike
48
48
  include Modelable
49
49
 
50
- FMregex = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
51
50
  DateMatcher = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
52
51
  Matcher = /^(.+\/)*(.*)(\.[^.]+)$/
53
52
 
@@ -88,44 +87,17 @@ module Ruhoh::Base
88
87
  !!@pointer['realpath']
89
88
  end
90
89
 
91
- # Primary method to parse the file as a page-like object.
92
- # File API is currently defines:
93
- # 1. Top YAML meta-data
94
- # 2. Page Body
95
- #
90
+ # See Ruhoh::Parse.page_file
96
91
  # @returns[Hash Object] processed top meta-data, raw (unconverted) content body
97
92
  def parse_page_file
98
93
  raise "File not found: #{@pointer['realpath']}" unless File.exist?(@pointer['realpath'])
99
-
100
- page = File.open(@pointer['realpath'], 'r:UTF-8') {|f| f.read }
101
-
102
- begin
103
- front_matter = page.match(FMregex)
104
- rescue => e
105
- raise "Error trying to read meta-data from #{@pointer['realpath']}." +
106
- " Check your folder configuration. Error details: #{e}"
107
- end
108
-
109
- data = front_matter ?
110
- (YAML.load(front_matter[0].gsub(/---\n/, "")) || {}) :
111
- {}
112
-
113
- result = {
114
- "data" => data,
115
- "content" => page.gsub(FMregex, '')
116
- }
94
+ result = Ruhoh::Parse.page_file(@pointer['realpath'])
117
95
 
118
96
  # variable cache
119
- @data = data
97
+ @data = result["data"]
120
98
  @content = result['content']
121
99
 
122
100
  result
123
- rescue Psych::SyntaxError => e
124
- Ruhoh.log.error("Psych::SyntaxError while parsing top YAML Metadata in #{ @pointer['realpath'] }\n" +
125
- "#{ e.message }\n" +
126
- "Try validating the YAML metadata using http://yamllint.com"
127
- )
128
- nil
129
101
  end
130
102
 
131
103
  def parse_page_filename(filename)
@@ -1,4 +1,3 @@
1
- require 'set'
2
1
  require 'ruhoh/summarizer'
3
2
 
4
3
  module Ruhoh::Base
@@ -47,7 +47,7 @@ class Ruhoh
47
47
  end
48
48
 
49
49
  def all
50
- (discover + registered).uniq
50
+ (discover + registered).to_a
51
51
  end
52
52
 
53
53
  def base
@@ -67,12 +67,19 @@ class Ruhoh
67
67
  end
68
68
 
69
69
  # discover all the resource mappings
70
+ # @return[Set]
70
71
  def discover
71
- FileUtils.cd(@ruhoh.base) {
72
- return Dir['*'].select { |x|
73
- File.directory?(x) && !["plugins", 'compiled'].include?(x)
74
- }
75
- }
72
+ results = Set.new
73
+
74
+ @ruhoh.cascade.each do |h|
75
+ FileUtils.cd(h["path"]) do
76
+ results += Dir['*'].select { |x|
77
+ File.directory?(x) && !["plugins", 'compiled'].include?(x)
78
+ }
79
+ end
80
+ end
81
+
82
+ results
76
83
  end
77
84
 
78
85
  def acting_as_pages
@@ -146,7 +153,7 @@ class Ruhoh
146
153
  else
147
154
  klass = camelize(type)
148
155
  Friend.say {
149
- red "#{resource} resource set to use:'#{type}' in config.yml but Ruhoh::Resources::#{klass} does not exist."
156
+ red "#{resource} resource set to use:'#{type}' in config but Ruhoh::Resources::#{klass} does not exist."
150
157
  }
151
158
  abort
152
159
  end
@@ -0,0 +1,85 @@
1
+ class Ruhoh
2
+ module Parse
3
+ TopYAMLregex = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
4
+ TopJSONregex = /^({\s*\n.*?\n?)^(}\s*$\n?)/m
5
+
6
+ # Primary method to parse the file as a page-like object.
7
+ # File API is currently defines:
8
+ # 1. Top meta-data
9
+ # 2. Page Body
10
+ #
11
+ # @returns[Hash Object] processed top meta-data, raw (unconverted) content body
12
+ def self.page_file(filepath)
13
+ result = {}
14
+ front_matter = nil
15
+ format = nil
16
+ page = File.open(filepath, 'r:UTF-8') { |f| f.read }
17
+ first_line = page.lines.first
18
+
19
+ begin
20
+ if (first_line.strip == '---')
21
+ front_matter = page.match(TopYAMLregex)
22
+ format = 'yaml'
23
+ elsif (first_line.strip == '{')
24
+ front_matter = page.match(TopJSONregex)
25
+ format = 'json'
26
+ end
27
+ rescue => e
28
+ raise "Error trying to read meta-data from #{ filepath }." +
29
+ " Check your folder configuration. Error details: #{e}"
30
+ end
31
+
32
+ if format == 'yaml'
33
+ data = yaml_for_pages(front_matter, filepath)
34
+ result["content"] = page.gsub(TopYAMLregex, '')
35
+ else
36
+ data = json_for_pages(front_matter, filepath)
37
+ result["content"] = page.gsub(TopJSONregex, '')
38
+ end
39
+
40
+ result["data"] = data
41
+ result
42
+ end
43
+
44
+ def self.data_file(*args)
45
+ base = File.__send__(:join, args)
46
+
47
+ filepath = nil
48
+ ["#{ base }.json", "#{ base }.yml", "#{ base }.yaml"].each do |result|
49
+ filepath = result and break if File.exist?(result)
50
+ end
51
+ return nil unless filepath
52
+
53
+ file = File.open(filepath, 'r:UTF-8') { |f| f.read }
54
+
55
+ File.extname(filepath) == ".json" ? json(file) : yaml(file)
56
+ end
57
+
58
+ def self.yaml(file)
59
+ YAML.load(file) || {}
60
+ rescue Psych::SyntaxError => e
61
+ Ruhoh.log.error("ERROR in #{filepath}: #{e.message}")
62
+ nil
63
+ end
64
+
65
+ def self.json(file)
66
+ JSON.load(file) || {}
67
+ end
68
+
69
+ def self.yaml_for_pages(front_matter, filepath)
70
+ return {} unless front_matter
71
+ YAML.load(front_matter[0].gsub(/---\n/, "")) || {}
72
+ rescue Psych::SyntaxError => e
73
+ Ruhoh.log.error("Psych::SyntaxError while parsing top YAML Metadata in #{ filepath }\n" +
74
+ "#{ e.message }\n" +
75
+ "Try validating the YAML metadata using http://yamllint.com"
76
+ )
77
+ nil
78
+ end
79
+
80
+ def self.json_for_pages(front_matter, filepath)
81
+ return {} unless front_matter
82
+ JSON.load(front_matter[0]) || {}
83
+ end
84
+ end
85
+ end
@@ -27,7 +27,7 @@ class Ruhoh
27
27
  yellow "Watch [#{Time.now.strftime("%H:%M:%S")}] [Update #{path}] : #{args.size} files changed"
28
28
  }
29
29
 
30
- if path == "config.yml"
30
+ if %w{ config.json config.yml config.yaml }.include?(path)
31
31
  ruhoh.config true
32
32
  else
33
33
  separator = File::ALT_SEPARATOR ?
@@ -38,10 +38,7 @@ class Ruhoh
38
38
  ruhoh.cache.delete(ruhoh.collection(resource).files_cache_key)
39
39
  ruhoh.cache.delete("#{ resource }-all")
40
40
 
41
- puts("HERE", resource)
42
-
43
41
  ruhoh.collection(resource).load_watcher.update(path)
44
- puts(ruhoh.collection(resource))
45
42
  end
46
43
  end
47
44
  end
@@ -2,8 +2,24 @@ module Ruhoh::Resources::Data
2
2
  class Collection
3
3
  include Ruhoh::Base::Collectable
4
4
 
5
+ # TODO: This is ugly but it works.
6
+ # Should handle data extensions in the cascade more elegantly
5
7
  def dictionary
6
- Ruhoh::Utils.parse_yaml_file(@ruhoh.paths.base, "#{resource_name}.yml") || {}
8
+ found_path_prefix = nil
9
+
10
+ @ruhoh.cascade.reverse.map do |h|
11
+ path_prefix = File.join(h["path"], resource_name)
12
+
13
+ ["#{ path_prefix }.json", "#{ path_prefix }.yml", "#{ path_prefix }.yaml"].each do |file|
14
+ found_path_prefix = path_prefix and break if File.exist?(file)
15
+ end
16
+
17
+ break if found_path_prefix
18
+ end
19
+
20
+ return {} unless found_path_prefix
21
+
22
+ Ruhoh::Parse.data_file(found_path_prefix) || {}
7
23
  end
8
24
  end
9
25
  end
@@ -6,18 +6,10 @@ module Ruhoh::Resources::Media
6
6
  # We can't use it now because there is automatic digest support
7
7
  # but currently no way to dynamically update all media links in views with digest path.
8
8
  def run
9
- collection = @collection
10
- unless @collection.paths?
11
- Ruhoh::Friend.say { yellow "#{collection.resource_name.capitalize}: directory not found - skipping." }
12
- return
13
- end
14
- Ruhoh::Friend.say { cyan "#{collection.resource_name.capitalize}: (copying valid files)" }
9
+ return unless setup_compilable
15
10
 
16
- compiled_path = Ruhoh::Utils.url_to_path(@ruhoh.to_url(@collection.url_endpoint), @ruhoh.paths.compiled)
17
- FileUtils.mkdir_p compiled_path
18
-
19
11
  @collection.files.values.each do |pointer|
20
- compiled_file = File.join(compiled_path, pointer['id'])
12
+ compiled_file = File.join(@collection.compiled_path, pointer['id'])
21
13
  FileUtils.mkdir_p File.dirname(compiled_file)
22
14
  FileUtils.cp_r pointer['realpath'], compiled_file
23
15
  Ruhoh::Friend.say { green " > #{pointer['id']}" }
@@ -7,17 +7,16 @@ module Ruhoh::Resources::Pages
7
7
  pages = @collection.all
8
8
  resource_name = @collection.resource_name
9
9
  Ruhoh::Friend.say { cyan "#{resource_name.capitalize}: (#{pages.count} #{resource_name})" }
10
-
11
- FileUtils.cd(@ruhoh.paths.compiled) {
12
- pages.each do |data|
13
- view = @ruhoh.master_view(data['pointer'])
14
10
 
15
- FileUtils.mkdir_p File.dirname(view.compiled_path)
16
- File.open(view.compiled_path, 'w:UTF-8') { |p| p.puts view.render_full }
11
+ pages.each do |data|
12
+ view = @ruhoh.master_view(data['pointer'])
13
+
14
+ FileUtils.mkdir_p File.dirname(view.compiled_path)
15
+ File.open(view.compiled_path, 'w:UTF-8') { |p| p.puts view.render_full }
16
+
17
+ Ruhoh::Friend.say { green " > #{data['id']}" }
18
+ end
17
19
 
18
- Ruhoh::Friend.say { green " > #{data['id']}" }
19
- end
20
- }
21
20
 
22
21
  pagination
23
22
  rss
@@ -35,23 +34,22 @@ module Ruhoh::Resources::Pages
35
34
  total_pages = (pages_count.to_f/config["per_page"]).ceil
36
35
 
37
36
  Ruhoh::Friend.say { cyan "#{resource_name} paginator: (#{total_pages} pages)" }
38
-
39
- FileUtils.cd(@ruhoh.paths.compiled) {
40
- total_pages.times.map { |i|
41
- # if a root page is defined we assume it's getting compiled elsewhere.
42
- next if (i.zero? && config["root_page"])
43
-
44
- url = "#{config["url"]}/#{i+1}"
45
- view = @ruhoh.master_view({"resource" => resource_name})
46
- view.page_data = {
47
- "layout" => config["layout"],
48
- "current_page" => (i+1),
49
- "url" => @ruhoh.to_url(url)
50
- }
51
- FileUtils.mkdir_p File.dirname(view.compiled_path)
52
- File.open(view.compiled_path, 'w:UTF-8') { |p| p.puts view.render_full }
53
- Ruhoh::Friend.say { green " > #{view.page_data['url']}" }
37
+
38
+ total_pages.times.map { |i|
39
+ # if a root page is defined we assume it's getting compiled elsewhere.
40
+ next if (i.zero? && config["root_page"])
41
+
42
+ url = "#{config["url"]}/#{i+1}"
43
+ view = @ruhoh.master_view({"resource" => resource_name})
44
+ view.page_data = {
45
+ "layout" => config["layout"],
46
+ "current_page" => (i+1),
47
+ "total_pages" => total_pages,
48
+ "url" => @ruhoh.to_url(url)
54
49
  }
50
+ FileUtils.mkdir_p File.dirname(view.compiled_path)
51
+ File.open(view.compiled_path, 'w:UTF-8') { |p| p.puts view.render_full }
52
+ Ruhoh::Friend.say { green " > #{view.page_data['url']}" }
55
53
  }
56
54
  end
57
55
 
@@ -88,15 +86,12 @@ module Ruhoh::Resources::Pages
88
86
  }
89
87
  end
90
88
 
91
- FileUtils.cd(@ruhoh.paths.compiled) {
92
- compiled_path = CGI.unescape(@ruhoh.to_url(config['url'], "rss.xml"))
93
- compiled_path = compiled_path.gsub(/^\//, '')
89
+ compiled_path = @ruhoh.compiled_path(@ruhoh.to_url(config['url'], "rss.xml"))
94
90
 
95
- FileUtils.mkdir_p File.dirname(compiled_path)
96
- File.open(compiled_path, 'w'){ |p| p.puts feed.to_xml }
91
+ FileUtils.mkdir_p File.dirname(compiled_path)
92
+ File.open(compiled_path, 'w'){ |p| p.puts feed.to_xml }
97
93
 
98
- Ruhoh::Friend.say { green " > #{compiled_path}" }
99
- }
94
+ Ruhoh::Friend.say { green " > #{compiled_path}" }
100
95
  end
101
96
  end
102
97
  end
@@ -9,19 +9,10 @@ module Ruhoh::Resources::Static
9
9
  #
10
10
  # @returns Nothing.
11
11
  def run
12
- collection = @collection
13
-
14
- unless @collection.paths?
15
- Ruhoh::Friend.say { yellow "#{collection.resource_name.capitalize}: directory not found - skipping." }
16
- return
17
- end
18
- Ruhoh::Friend.say { cyan "#{collection.resource_name.capitalize}: (copying valid files)" }
19
-
20
- compiled_path = Ruhoh::Utils.url_to_path(@ruhoh.to_url(@collection.url_endpoint), @ruhoh.paths.compiled)
21
- FileUtils.mkdir_p compiled_path
12
+ return unless setup_compilable
22
13
 
23
14
  @collection.files.values.each do |pointer|
24
- compiled_file = File.join(compiled_path, pointer['id'])
15
+ compiled_file = File.join(@collection.compiled_path, pointer['id'])
25
16
 
26
17
  FileUtils.mkdir_p File.dirname(compiled_file)
27
18
  FileUtils.cp_r pointer['realpath'], compiled_file
@@ -2,27 +2,14 @@ module Ruhoh::Resources::Theme
2
2
  class Compiler
3
3
  include Ruhoh::Base::Compilable
4
4
 
5
- def run
6
- copy
7
- end
8
-
9
5
  # Copies all assets over to the compiled site.
10
6
  # Note the compiled assets are namespaced at /assets/
11
- def copy
12
- collection = @collection
13
- unless @collection.paths?
14
- Ruhoh::Friend.say { yellow "#{collection.resource_name.capitalize}: directory not found - skipping." }
15
- return
16
- end
17
-
18
- Ruhoh::Friend.say { cyan "Theme: ('#{collection.resource_name}' copying non-resource files)" }
19
-
20
- theme = Ruhoh::Utils.url_to_path(@collection.url_endpoint, @ruhoh.paths.compiled)
21
- FileUtils.mkdir_p theme
7
+ def run
8
+ return unless setup_compilable
22
9
 
23
10
  self.files.each do |file|
24
11
  original_file = File.join(@ruhoh.paths.theme, file)
25
- compiled_file = File.join(theme, file)
12
+ compiled_file = File.join(@collection.compiled_path, file)
26
13
  FileUtils.mkdir_p File.dirname(compiled_file)
27
14
  FileUtils.cp_r original_file, compiled_file
28
15
  Ruhoh::Friend.say { green " > #{file}" }
@@ -39,7 +26,7 @@ module Ruhoh::Resources::Theme
39
26
  }
40
27
  end
41
28
 
42
- # Checks a given asset filepath against any user-defined exclusion rules in theme.yml
29
+ # Checks a given asset filepath against any user-defined exclusion rules in config
43
30
  # Omit layouts, stylesheets, javascripts, media as they are handled by their respective resources.
44
31
  # @returns[Boolean]
45
32
  def is_valid_asset?(filepath)
@@ -9,7 +9,7 @@ module Ruhoh::Resources::Widgets
9
9
  model = find("#{ name }/#{ (widget_config['use'] || "default") }")
10
10
  return '' unless model
11
11
 
12
- # merge the config.yml data into the inline layout data.
12
+ # merge the config data into the inline layout data.
13
13
  # Note this is reversing the normal hierarchy
14
14
  # in that inline should always override config level.
15
15
  # However the inline in this case is set as implementation defaults
@@ -3,21 +3,13 @@ module Ruhoh::Resources::Widgets
3
3
  include Ruhoh::Base::Compilable
4
4
 
5
5
  def run
6
- collection = @collection
7
- unless @collection.paths?
8
- Ruhoh::Friend.say { yellow "#{collection.resource_name.capitalize}: directory not found - skipping." }
9
- return
10
- end
11
- Ruhoh::Friend.say { cyan "#{collection.resource_name.capitalize}: (copying valid files)" }
12
-
13
- compiled_path = Ruhoh::Utils.url_to_path(@ruhoh.to_url(@collection.url_endpoint), @ruhoh.paths.compiled)
14
- FileUtils.mkdir_p compiled_path
6
+ return unless setup_compilable
15
7
 
16
- files = collection.files.values
8
+ files = @collection.files.values
17
9
  files.delete_if { |p| !is_valid_file? (p['id']) }
18
10
 
19
11
  files.each do |pointer|
20
- compiled_file = File.join(compiled_path, pointer['id'])
12
+ compiled_file = File.join(@collection.compiled_path, pointer['id'])
21
13
  FileUtils.mkdir_p File.dirname(compiled_file)
22
14
  FileUtils.cp_r pointer['realpath'], compiled_file
23
15
  Ruhoh::Friend.say { green " > #{pointer['id']}" }
@@ -1,26 +1,5 @@
1
1
  class Ruhoh
2
2
  module Utils
3
-
4
- FMregex = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
5
-
6
- def self.parse_yaml_file(*args)
7
- filepath = File.__send__ :join, args
8
- return nil unless File.exist? filepath
9
-
10
- file = File.open(filepath, 'r:UTF-8') {|f| f.read }
11
- yaml = YAML.load(file) || {}
12
- yaml
13
- rescue Psych::SyntaxError => e
14
- Ruhoh.log.error("ERROR in #{filepath}: #{e.message}")
15
- nil
16
- end
17
-
18
- def self.url_to_path(url, base=nil)
19
- url = url.gsub(/^\//, '')
20
- parts = url.split('/')
21
- parts = parts.unshift(base) if base
22
- File.__send__(:join, parts)
23
- end
24
3
 
25
4
  def self.report(name, collection, invalid)
26
5
  output = "#{collection.count}/#{collection.count + invalid.count} #{name} processed."
@@ -1,4 +1,4 @@
1
1
  class Ruhoh
2
- Version = VERSION = '2.3'
2
+ Version = VERSION = '2.4'
3
3
  RuhohSpec = '2.1'
4
4
  end
@@ -3,7 +3,6 @@ require 'ruhoh/views/rmustache'
3
3
  module Ruhoh::Views
4
4
  module Helpers ; end
5
5
  class MasterView < RMustache
6
- attr_reader :sub_layout, :master_layout
7
6
  attr_accessor :page_data
8
7
 
9
8
  def initialize(ruhoh, pointer_or_data)
@@ -27,8 +26,13 @@ module Ruhoh::Views
27
26
  end
28
27
 
29
28
  def render_full
30
- process_layouts
31
- render(expand_layouts)
29
+ if page_layouts.empty?
30
+ render_content
31
+ else
32
+ page_layouts.drop(1).reduce(render(page_layouts.first.content)) do |c, l|
33
+ render(l.content, :content => c)
34
+ end
35
+ end
32
36
  end
33
37
 
34
38
  def render_content
@@ -104,7 +108,7 @@ module Ruhoh::Views
104
108
  #
105
109
  # Returns: [String] The relative path to the compiled file for this page.
106
110
  def compiled_path
107
- path = CGI.unescape(@page_data['url']).gsub(/^\//, '') #strip leading slash.
111
+ path = @ruhoh.compiled_path(@page_data['url'])
108
112
  path = "index.html" if path.empty?
109
113
  path += '/index.html' unless path =~ /\.\w+$/
110
114
  path
@@ -112,38 +116,31 @@ module Ruhoh::Views
112
116
 
113
117
  protected
114
118
 
115
- def process_layouts
116
- if @page_data['layout']
117
- @sub_layout = layouts.find(@page_data['layout'], :all => true)
118
- raise "Layout does not exist: #{@page_data['layout']}" unless @sub_layout
119
+ def page_layouts
120
+ return @page_layouts unless @page_layouts.nil?
121
+
122
+ layout = if @page_data['layout']
123
+ layouts.find(@page_data['layout'], :all => true) or raise "Layout does not exist: #{@page_data['layout']}"
119
124
  elsif @page_data['layout'] != false
120
125
  # try default
121
- @sub_layout = layouts.find(@pointer["resource"], :all => true)
126
+ layouts.find(@pointer['resource'], :all => true)
122
127
  end
123
128
 
124
- if @sub_layout && @sub_layout.layout
125
- @master_layout = layouts.find(@sub_layout.layout)
126
- raise "Layout does not exist: #{ @sub_layout.layout }" unless @master_layout
127
- end
128
- end
129
+ @page_layouts = if layout.nil?
130
+ []
131
+ else
132
+ page_layouts = [layout]
133
+ until layout.layout.nil?
134
+ layout = layouts.find(layout.layout) or raise "Layout does not exist: #{layout.layout}"
129
135
 
130
- # Expand the layout(s).
131
- # Pages may have a single master_layout, a master_layout + sub_layout, or no layout.
132
- def expand_layouts
133
- if @sub_layout
134
- layout = @sub_layout.content
136
+ raise "Layout cycle detected when rendering #{@pointer}: \n #{
137
+ (page_layouts<<layout).map{|l| l.pointer["realpath"]}.join("\n")
138
+ }" if page_layouts.include?(layout)
135
139
 
136
- # If a master_layout is found we need to process the sub_layout
137
- # into the master_layout using mustache.
138
- if @master_layout && @master_layout.content
139
- layout = render(@master_layout.content, {"content" => layout})
140
+ page_layouts << layout
140
141
  end
141
- else
142
- # Minimum layout if no layout defined.
143
- layout = page ? '{{{ page.content }}}' : '{{{ content }}}'
142
+ page_layouts
144
143
  end
145
-
146
- layout
147
144
  end
148
145
 
149
146
  private
@@ -184,4 +181,4 @@ module Ruhoh::Views
184
181
  }.compact
185
182
  end
186
183
  end
187
- end
184
+ end
@@ -21,10 +21,9 @@ module Ruhoh::SprocketsPlugin
21
21
  env.append_path(path)
22
22
  end
23
23
 
24
- compiled_path = Ruhoh::Utils.url_to_path(@ruhoh.to_url(collection.url_endpoint), @ruhoh.paths.compiled)
25
- FileUtils.mkdir_p compiled_path
24
+ compile_collection_path
26
25
 
27
- manifest = Sprockets::Manifest.new(env, compiled_path)
26
+ manifest = Sprockets::Manifest.new(env, @collection.compiled_path)
28
27
  assets = collection.files.values.map{ |p|
29
28
  Ruhoh::Friend.say { green " > #{p['id']}" }
30
29
  p["id"]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruhoh
3
3
  version: !ruby/object:Gem::Version
4
- version: '2.3'
4
+ version: '2.4'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-25 00:00:00.000000000 Z
12
+ date: 2013-09-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -151,6 +151,7 @@ files:
151
151
  - Rakefile
152
152
  - bin/ruhoh
153
153
  - cucumber.yml
154
+ - features/base_path.feature
154
155
  - features/categories.feature
155
156
  - features/config.feature
156
157
  - features/conversion.feature
@@ -158,6 +159,7 @@ files:
158
159
  - features/drafts.feature
159
160
  - features/ignore.feature
160
161
  - features/javascripts.feature
162
+ - features/json.feature
161
163
  - features/layouts.feature
162
164
  - features/pagination.feature
163
165
  - features/partials.feature
@@ -188,6 +190,7 @@ files:
188
190
  - lib/ruhoh/converters/markdown.rb
189
191
  - lib/ruhoh/friend.rb
190
192
  - lib/ruhoh/logger.rb
193
+ - lib/ruhoh/parse.rb
191
194
  - lib/ruhoh/programs/compile.rb
192
195
  - lib/ruhoh/programs/preview.rb
193
196
  - lib/ruhoh/programs/watch.rb
@@ -274,18 +277,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
274
277
  - - ! '>='
275
278
  - !ruby/object:Gem::Version
276
279
  version: '0'
277
- segments:
278
- - 0
279
- hash: 897888631782695949
280
280
  required_rubygems_version: !ruby/object:Gem::Requirement
281
281
  none: false
282
282
  requirements:
283
283
  - - ! '>='
284
284
  - !ruby/object:Gem::Version
285
285
  version: '0'
286
- segments:
287
- - 0
288
- hash: 897888631782695949
289
286
  requirements: []
290
287
  rubyforge_project:
291
288
  rubygems_version: 1.8.24