ruhoh 2.3 → 2.4

Sign up to get free protection for your applications and to get access to all the features.
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