octopress 3.0.0.alpha8 → 3.0.0.rc.1

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.travis.yml +5 -0
  4. data/README.md +130 -27
  5. data/lib/octopress.rb +8 -2
  6. data/lib/octopress/command.rb +3 -0
  7. data/lib/octopress/commands/build.rb +1 -4
  8. data/lib/octopress/commands/doctor.rb +3 -7
  9. data/lib/octopress/commands/helpers.rb +8 -3
  10. data/lib/octopress/commands/init.rb +15 -0
  11. data/lib/octopress/commands/new.rb +25 -22
  12. data/lib/octopress/commands/publish.rb +3 -1
  13. data/lib/octopress/commands/serve.rb +4 -7
  14. data/lib/octopress/configuration.rb +20 -12
  15. data/lib/octopress/draft.rb +39 -8
  16. data/lib/octopress/{core_ext.rb → ext/hash.rb} +0 -0
  17. data/lib/octopress/ext/titlecase.rb +37 -0
  18. data/lib/octopress/page.rb +49 -33
  19. data/lib/octopress/post.rb +17 -16
  20. data/lib/octopress/scaffold.rb +26 -0
  21. data/lib/octopress/version.rb +1 -1
  22. data/octopress.gemspec +1 -1
  23. data/scaffold/_octopress.yml +11 -0
  24. data/scaffold/_templates/page +6 -0
  25. data/scaffold/_templates/post +6 -0
  26. data/test/expected/_drafts/stupid-idea.markdown +6 -0
  27. data/test/expected/_layouts/page.html +1 -0
  28. data/test/expected/_layouts/post.html +1 -0
  29. data/test/expected/_octopress.yml +11 -0
  30. data/test/expected/_posts/2014-03-11-idea.markdown +6 -0
  31. data/test/expected/_posts/2014-03-12-awesome-stuff.markdown +6 -0
  32. data/test/expected/_posts/2014-03-13-awesome.markdown +6 -0
  33. data/test/expected/_site/2014/03/11/idea.html +1 -0
  34. data/test/expected/_site/2014/03/12/awesome-stuff.html +1 -0
  35. data/test/expected/_site/2014/03/13/awesome.html +1 -0
  36. data/test/expected/_site/awesome-page.html +1 -0
  37. data/test/expected/_site/cool-page.html +1 -0
  38. data/test/expected/_site/index.html +0 -0
  39. data/test/expected/_site/okay-page/index.html +1 -0
  40. data/test/expected/_templates/page +6 -0
  41. data/test/expected/_templates/post +6 -0
  42. data/test/expected/awesome-page.html +5 -0
  43. data/test/expected/cool-page.html +5 -0
  44. data/test/expected/index.html +0 -0
  45. data/test/expected/okay-page/index.html +5 -0
  46. data/test/test.rb +100 -0
  47. data/test/test_suite.rb +107 -0
  48. metadata +57 -10
  49. data/docs/_octopress.yml +0 -1
  50. data/docs/index.html +0 -1
  51. data/lib/octopress/commands/docs.rb +0 -74
@@ -2,10 +2,10 @@ module Octopress
2
2
  module Configuration
3
3
 
4
4
  DEFAULTS = {
5
- 'new_post_extension' => 'markdown',
6
- 'new_page_extension' => 'html',
7
- 'new_post_layout' => 'post',
8
- 'new_page_layout' => 'page',
5
+ 'post_ext' => 'markdown',
6
+ 'page_ext' => 'html',
7
+ 'post_layout' => 'post',
8
+ 'page_layout' => 'page',
9
9
  'titlecase' => true
10
10
  }
11
11
 
@@ -13,22 +13,30 @@ module Octopress
13
13
  return @config if @config
14
14
 
15
15
  file = options['octopress-config'] || '_octopress.yml'
16
- config = {}
16
+ user_config = {}
17
+
17
18
  if File.exist? file
18
- config = YAML.safe_load(File.open(file))
19
+ user_config = YAML.safe_load(File.open(file).read) || {}
19
20
  end
20
- @config = DEFAULTS.deep_merge(config)
21
+
22
+ user_config = user_config.deep_merge(options['override'] || {})
23
+ user_config = (options['defaults'] || {}).deep_merge(user_config)
24
+
25
+ @config = DEFAULTS.deep_merge(user_config)
21
26
  end
22
27
 
23
28
  def self.jekyll_config(options={})
24
29
  return @jekyll_config if @jekyll_config
25
30
 
26
- log_level = Jekyll.logger.log_level
27
- Jekyll.logger.log_level = Jekyll::Stevenson::WARN
28
- jekyll_config = Jekyll.configuration(options)
29
- Jekyll.logger.log_level = log_level
31
+ configs = Jekyll::Configuration::DEFAULTS
32
+
33
+ (options['config'] || ['_config.yml']).each do |file|
34
+ if File.exist? file
35
+ configs = configs.deep_merge YAML.safe_load(File.open(file))
36
+ end
37
+ end
30
38
 
31
- @jekyll_config = jekyll_config
39
+ @jekyll_config = configs
32
40
  end
33
41
  end
34
42
  end
@@ -1,40 +1,71 @@
1
1
  module Octopress
2
2
  class Draft < Post
3
3
 
4
+ def set_default_options
5
+ super
6
+ @options['type'] = 'draft'
7
+ end
8
+
9
+ def path
10
+ name = "#{title_slug}.#{extension}"
11
+ File.join(source, '_drafts', name)
12
+ end
13
+
14
+ # -----
15
+ # Methods for publishing drafts
16
+ # -----
17
+
18
+ # Create a new post from draft file
19
+ #
20
+ # Sets post options based on draft file contents
21
+ # and options passed to the publish command
22
+ #
4
23
  def publish
24
+
5
25
  post_options = {
6
26
  'title' => read_draft_title,
27
+ 'slug' => publish_slug,
28
+ 'date' => @options['date'],
7
29
  'content' => read_draft_content,
8
30
  'type' => 'post from draft'
9
31
  }
32
+
33
+ # Create a new post file
34
+ #
10
35
  Post.new(post_options).write
36
+
37
+ # Remove the old draft file
38
+ #
11
39
  FileUtils.rm @options['path']
12
- end
13
40
 
14
- def set_default_options
15
- super
16
- @options['type'] = 'draft'
17
41
  end
18
42
 
19
- def path
20
- name = "#{title_slug}.#{extension}"
21
- File.join(source, '_drafts', name)
43
+ # Get the slug from options or filename
44
+ #
45
+ def publish_slug
46
+ @options['slug'] || File.basename(@options['path'], '.*')
22
47
  end
23
48
 
49
+ # Reads the file from _drafts/[path]
50
+ #
24
51
  def read
25
52
  if @draft_content
26
53
  @draft_content
27
54
  else
28
55
  file = @options['path']
29
56
  abort "File #{file} not found." if !File.exist? file
30
- @draft_content = Pathname.new(file).read
57
+ @draft_content = File.open(file).read
31
58
  end
32
59
  end
33
60
 
61
+ # Get title from draft post file
62
+ #
34
63
  def read_draft_title
35
64
  read.match(/title:\s+(.+)?$/)[1]
36
65
  end
37
66
 
67
+ # Get content from draft post file
68
+ #
38
69
  def read_draft_content
39
70
  read.sub(/date:\s+.+?$/, "date: #{@options['date']}")
40
71
  end
@@ -0,0 +1,37 @@
1
+ class String
2
+ def titlecase
3
+ small_words = %w(a an and as at but by en for if in of on or the to v v. via vs vs.)
4
+
5
+ x = split(" ").map do |word|
6
+ # note: word could contain non-word characters!
7
+ # downcase all small_words, capitalize the rest
8
+ small_words.include?(word.gsub(/\W/, "").downcase) ? word.downcase! : word.smart_capitalize!
9
+ word
10
+ end
11
+ # capitalize first and last words
12
+ x.first.to_s.smart_capitalize!
13
+ x.last.to_s.smart_capitalize!
14
+ # small words are capitalized after colon, period, exclamation mark, question mark
15
+ x.join(" ").gsub(/(:|\.|!|\?)\s?(\W*#{small_words.join("|")}\W*)\s/) { "#{$1} #{$2.smart_capitalize} " }
16
+ end
17
+
18
+ def titlecase!
19
+ replace(titlecase)
20
+ end
21
+
22
+ def smart_capitalize
23
+ target = dup
24
+ # ignore any leading crazy characters and capitalize the first real character
25
+ if target =~ /^['"\(\[']*([a-z])/
26
+ i = index($1)
27
+ x = target[i,target.length]
28
+ # word with capitals and periods mid-word are left alone
29
+ target[i,1] = target[i,1].upcase unless x =~ /[A-Z]/ or x =~ /\.\w+/
30
+ end
31
+ target
32
+ end
33
+
34
+ def smart_capitalize!
35
+ replace(smart_capitalize)
36
+ end
37
+ end
@@ -5,6 +5,15 @@ module Octopress
5
5
  @config = Octopress.config(options)
6
6
  @options = options
7
7
  set_default_options
8
+
9
+ # Ensure title
10
+ #
11
+ @options['title'] ||= ''
12
+
13
+ # Ensure a quoted title
14
+ #
15
+ @options['title'] = "\"#{@options['title']}\""
16
+
8
17
  @content = options['content'] || content
9
18
  end
10
19
 
@@ -37,9 +46,11 @@ module Octopress
37
46
  raise "You must specify a path." unless file
38
47
 
39
48
  # If path ends with a slash, make it an index
49
+ #
40
50
  file += "index" if file =~ /\/$/
41
51
 
42
52
  # if path has no extension, add the default extension
53
+ #
43
54
  file += ".#{extension}" unless file =~ /\.\w+$/
44
55
 
45
56
  @path = File.join(source, file)
@@ -50,31 +61,44 @@ module Octopress
50
61
  end
51
62
 
52
63
  def set_default_options
53
- @options['type'] ||= 'page'
54
- @options['layout'] = @config['new_page_layout']
64
+ @options['type'] ||= 'page'
65
+ @options['layout'] = @config['page_layout']
55
66
  @options['date'] = convert_date @options['date']
56
- @options['extension'] ||= @config['new_page_extension']
57
- @options['template'] ||= @config['new_page_template']
67
+ @options['extension'] ||= @config['page_ext']
68
+ @options['template'] ||= @config['page_template']
58
69
  end
59
70
 
60
71
  def convert_date(date)
61
72
  if date
62
- begin
63
- Time.parse(date.to_s).iso8601
64
- rescue => error
65
- abort 'Could not parse date. Try formatting it like YYYY-MM-DD HH:MM'
73
+ if @options['date'] == 'now'
74
+ @options['date'] = Time.now.iso8601
75
+ else
76
+ begin
77
+ Time.parse(date.to_s).iso8601
78
+ rescue => error
79
+ puts 'Could not parse date. Try formatting it like YYYY-MM-DD HH:MM'
80
+ abort error.message
81
+ end
66
82
  end
67
83
  end
68
84
  end
69
85
 
70
- # Load the user provide or default template for a new post.
86
+ # Load the user provide or default template for a new post or page.
71
87
  #
72
88
  def content
89
+
90
+ # Handle case where user passes the full path
91
+ #
73
92
  file = @options['template']
74
- file = File.join(source, '_templates', file) if file
75
- if file
76
- abort "No template found at #{file}" unless File.exist? file
77
- parse_template Pathname.new(file).read
93
+
94
+ if file
95
+ file.sub(/^_templates\//, '')
96
+ file = File.join(source, '_templates', file) if file
97
+ if File.exist? file
98
+ parse_template File.open(file).read
99
+ else
100
+ abort "No #{@options['type']} template found at #{file}"
101
+ end
78
102
  else
79
103
  parse_template default_content
80
104
  end
@@ -83,45 +107,37 @@ module Octopress
83
107
  # Render Liquid vars in YAML front-matter.
84
108
  def parse_template(input)
85
109
 
110
+ @options['title'].titlecase! if @config['titlecase']
86
111
  # If possible only parse the YAML front matter.
87
112
  # If YAML front-matter dashes aren't present parse the whole
88
113
  # template and add dashes.
89
114
  #
90
115
  parsed = if input =~ /\A-{3}\s+(.+?)\s+-{3}\s+(.+)/m
91
116
  template = Liquid::Template.parse($1)
92
- "---\n#{template.render(@options)}\n---\n\n#{$2}"
117
+ "---\n#{template.render(@options).strip}\n---\n\n#{$2}"
93
118
  else
94
119
  template = Liquid::Template.parse(input)
95
- "---\n#{template.render(@options)}\n---\n\n"
120
+ "---\n#{template.render(@options).strip}\n---\n\n"
96
121
  end
97
122
  end
98
123
 
99
124
  def date_slug
100
- Time.parse(@options['date']).strftime('%Y-%m-%d')
125
+ @options['date'].split('T')[0]
101
126
  end
102
127
 
103
- # Returns a string which is url compatible.
104
- #
105
- def title_slug
106
- value = @options['title'].gsub(/[^\x00-\x7F]/u, '')
107
- value.gsub!(/(&amp;|&)+/, 'and')
108
- value.gsub!(/[']+/, '')
109
- value.gsub!(/\W+/, ' ')
110
- value.strip!
111
- value.downcase!
112
- value.gsub!(' ', '-')
113
- value
128
+ def front_matter(vars)
129
+ fm = []
130
+ vars.each do |v|
131
+ fm << "#{v}: {{ #{v} }}" if @options[v]
132
+ end
133
+ fm.join("\n")
114
134
  end
115
135
 
116
136
  # Page template defaults
117
137
  #
118
138
  def default_content
119
- <<-TEMPLATE
120
- ---
121
- layout: {{ layout }}
122
- title: {{ title }}
123
- ---
124
- TEMPLATE
139
+ front_matter %w{layout title date}
125
140
  end
141
+
126
142
  end
127
143
  end
@@ -2,12 +2,12 @@ module Octopress
2
2
  class Post < Page
3
3
 
4
4
  def set_default_options
5
- @options['type'] ||= 'post'
6
- @options['layout'] = @config['new_post_layout']
7
- @options['date'] = convert_date @options['date'] || Time.now
8
- @options['extension'] ||= @config['new_post_extension']
9
- @options['template'] ||= @config['new_post_template']
10
- raise "You must specify a title." if @options['title'].nil?
5
+ @options['type'] ||= 'post'
6
+ @options['layout'] = @config['post_layout']
7
+ @options['date'] ||= Time.now.iso8601
8
+ @options['date'] = convert_date @options['date']
9
+ @options['extension'] ||= @config['post_ext']
10
+ @options['template'] ||= @config['post_template']
11
11
  end
12
12
 
13
13
  def path
@@ -15,17 +15,18 @@ module Octopress
15
15
  File.join(source, '_posts', name)
16
16
  end
17
17
 
18
- # Post template defaults
18
+ # Returns a string which is url compatible.
19
19
  #
20
- def default_content
21
- <<-TEMPLATE
22
- ---
23
- layout: {{ layout }}
24
- title: {{ title }}
25
- date: {{ date }}
26
- categories: {{ categories }}
27
- ---
28
- TEMPLATE
20
+ def title_slug
21
+ value = (@options['slug'] || @options['title']).downcase
22
+ value.gsub!(/[^\x00-\x7F]/u, '')
23
+ value.gsub!(/(&amp;|&)+/, 'and')
24
+ value.gsub!(/[']+/, '')
25
+ value.gsub!(/\W+/, ' ')
26
+ value.strip!
27
+ value.gsub!(' ', '-')
28
+ value
29
29
  end
30
+
30
31
  end
31
32
  end
@@ -0,0 +1,26 @@
1
+ module Octopress
2
+ class Scaffold
3
+ attr_reader :path, :force
4
+
5
+ def initialize(args, options)
6
+ @path = File.expand_path(args.join(" "), Dir.pwd)
7
+ @force = !!options['force']
8
+ end
9
+
10
+ def write
11
+ if !force && (File.exist?(path + '/_templates') ||
12
+ File.exist?(path + '/_octopress.yml'))
13
+ abort "Some files already exist. Use --force to overwrite."
14
+ end
15
+
16
+ FileUtils.cp_r scaffold_path + '/.', path
17
+
18
+ puts "Octopress scaffold added to #{path}."
19
+ end
20
+
21
+ def scaffold_path
22
+ Octopress.expand_gem_path('scaffold')
23
+ end
24
+
25
+ end
26
+ end
@@ -1,3 +1,3 @@
1
1
  module Octopress
2
- VERSION = "3.0.0.alpha8"
2
+ VERSION = "3.0.0.rc.1"
3
3
  end
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_runtime_dependency "mercenary", "~> 0.3.1"
22
- spec.add_runtime_dependency "jekyll", "~> 1.4.2"
22
+ spec.add_runtime_dependency "jekyll", "~> 1.4.3"
23
23
 
24
24
  spec.add_development_dependency "bundler", "~> 1.3"
25
25
  spec.add_development_dependency "pry-debugger"
@@ -0,0 +1,11 @@
1
+ # Default extension for new posts and pages
2
+ post_ext: markdown
3
+ page_ext: html
4
+
5
+ # Default templates for posts and pages
6
+ # Found in _templates/
7
+ post_layout: post
8
+ page_layout: page
9
+
10
+ # Format titles with titlecase?
11
+ titlecase: true
@@ -0,0 +1,6 @@
1
+ ---
2
+ layout: {{ layout }}
3
+ title: {{ title }}
4
+ date: {{ date }}
5
+ ---
6
+
@@ -0,0 +1,6 @@
1
+ ---
2
+ layout: {{ layout }}
3
+ title: {{ title }}
4
+ date: {{ date }}
5
+ ---
6
+
@@ -0,0 +1,6 @@
1
+ ---
2
+ layout: post
3
+ title: "Stupid Idea"
4
+ date: 2014-03-10T15:20:00Z
5
+ ---
6
+
@@ -0,0 +1 @@
1
+ <div class='page'>{{ page.title }}{{ content }}</div>
@@ -0,0 +1 @@
1
+ <div class='post'>{{ page.title }}{{ content }}</div>