bunto 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.markdown +59 -0
  4. data/bin/bunto +51 -0
  5. data/lib/bunto.rb +179 -0
  6. data/lib/bunto/cleaner.rb +105 -0
  7. data/lib/bunto/collection.rb +205 -0
  8. data/lib/bunto/command.rb +65 -0
  9. data/lib/bunto/commands/build.rb +77 -0
  10. data/lib/bunto/commands/clean.rb +42 -0
  11. data/lib/bunto/commands/doctor.rb +114 -0
  12. data/lib/bunto/commands/help.rb +31 -0
  13. data/lib/bunto/commands/new.rb +82 -0
  14. data/lib/bunto/commands/serve.rb +204 -0
  15. data/lib/bunto/commands/serve/servlet.rb +61 -0
  16. data/lib/bunto/configuration.rb +323 -0
  17. data/lib/bunto/converter.rb +48 -0
  18. data/lib/bunto/converters/identity.rb +21 -0
  19. data/lib/bunto/converters/markdown.rb +92 -0
  20. data/lib/bunto/converters/markdown/kramdown_parser.rb +117 -0
  21. data/lib/bunto/converters/markdown/rdiscount_parser.rb +33 -0
  22. data/lib/bunto/converters/markdown/redcarpet_parser.rb +102 -0
  23. data/lib/bunto/converters/smartypants.rb +34 -0
  24. data/lib/bunto/convertible.rb +297 -0
  25. data/lib/bunto/deprecator.rb +46 -0
  26. data/lib/bunto/document.rb +444 -0
  27. data/lib/bunto/drops/bunto_drop.rb +21 -0
  28. data/lib/bunto/drops/collection_drop.rb +22 -0
  29. data/lib/bunto/drops/document_drop.rb +27 -0
  30. data/lib/bunto/drops/drop.rb +176 -0
  31. data/lib/bunto/drops/site_drop.rb +38 -0
  32. data/lib/bunto/drops/unified_payload_drop.rb +25 -0
  33. data/lib/bunto/drops/url_drop.rb +83 -0
  34. data/lib/bunto/entry_filter.rb +72 -0
  35. data/lib/bunto/errors.rb +10 -0
  36. data/lib/bunto/excerpt.rb +127 -0
  37. data/lib/bunto/external.rb +59 -0
  38. data/lib/bunto/filters.rb +367 -0
  39. data/lib/bunto/frontmatter_defaults.rb +188 -0
  40. data/lib/bunto/generator.rb +3 -0
  41. data/lib/bunto/hooks.rb +101 -0
  42. data/lib/bunto/layout.rb +49 -0
  43. data/lib/bunto/liquid_extensions.rb +22 -0
  44. data/lib/bunto/liquid_renderer.rb +39 -0
  45. data/lib/bunto/liquid_renderer/file.rb +50 -0
  46. data/lib/bunto/liquid_renderer/table.rb +94 -0
  47. data/lib/bunto/log_adapter.rb +115 -0
  48. data/lib/bunto/mime.types +800 -0
  49. data/lib/bunto/page.rb +180 -0
  50. data/lib/bunto/plugin.rb +96 -0
  51. data/lib/bunto/plugin_manager.rb +95 -0
  52. data/lib/bunto/post.rb +329 -0
  53. data/lib/bunto/publisher.rb +21 -0
  54. data/lib/bunto/reader.rb +126 -0
  55. data/lib/bunto/readers/collection_reader.rb +20 -0
  56. data/lib/bunto/readers/data_reader.rb +69 -0
  57. data/lib/bunto/readers/layout_reader.rb +53 -0
  58. data/lib/bunto/readers/page_reader.rb +21 -0
  59. data/lib/bunto/readers/post_reader.rb +62 -0
  60. data/lib/bunto/readers/static_file_reader.rb +21 -0
  61. data/lib/bunto/regenerator.rb +175 -0
  62. data/lib/bunto/related_posts.rb +56 -0
  63. data/lib/bunto/renderer.rb +191 -0
  64. data/lib/bunto/site.rb +391 -0
  65. data/lib/bunto/static_file.rb +141 -0
  66. data/lib/bunto/stevenson.rb +58 -0
  67. data/lib/bunto/tags/highlight.rb +122 -0
  68. data/lib/bunto/tags/include.rb +190 -0
  69. data/lib/bunto/tags/post_url.rb +88 -0
  70. data/lib/bunto/url.rb +136 -0
  71. data/lib/bunto/utils.rb +287 -0
  72. data/lib/bunto/utils/ansi.rb +59 -0
  73. data/lib/bunto/utils/platforms.rb +30 -0
  74. data/lib/bunto/version.rb +3 -0
  75. data/lib/site_template/.gitignore +3 -0
  76. data/lib/site_template/_config.yml +21 -0
  77. data/lib/site_template/_includes/footer.html +38 -0
  78. data/lib/site_template/_includes/head.html +12 -0
  79. data/lib/site_template/_includes/header.html +27 -0
  80. data/lib/site_template/_includes/icon-github.html +1 -0
  81. data/lib/site_template/_includes/icon-github.svg +1 -0
  82. data/lib/site_template/_includes/icon-twitter.html +1 -0
  83. data/lib/site_template/_includes/icon-twitter.svg +1 -0
  84. data/lib/site_template/_layouts/default.html +20 -0
  85. data/lib/site_template/_layouts/page.html +14 -0
  86. data/lib/site_template/_layouts/post.html +15 -0
  87. data/lib/site_template/_posts/0000-00-00-welcome-to-bunto.markdown.erb +25 -0
  88. data/lib/site_template/_sass/_base.scss +206 -0
  89. data/lib/site_template/_sass/_layout.scss +242 -0
  90. data/lib/site_template/_sass/_syntax-highlighting.scss +71 -0
  91. data/lib/site_template/about.md +15 -0
  92. data/lib/site_template/css/main.scss +53 -0
  93. data/lib/site_template/feed.xml +30 -0
  94. data/lib/site_template/index.html +23 -0
  95. metadata +252 -0
@@ -0,0 +1,188 @@
1
+ module Bunto
2
+ # This class handles custom defaults for YAML frontmatter settings.
3
+ # These are set in _config.yml and apply both to internal use (e.g. layout)
4
+ # and the data available to liquid.
5
+ #
6
+ # It is exposed via the frontmatter_defaults method on the site class.
7
+ class FrontmatterDefaults
8
+ # Initializes a new instance.
9
+ def initialize(site)
10
+ @site = site
11
+ end
12
+
13
+ def update_deprecated_types(set)
14
+ return set unless set.key?('scope') && set['scope'].key?('type')
15
+
16
+ set['scope']['type'] =
17
+ case set['scope']['type']
18
+ when 'page'
19
+ Deprecator.defaults_deprecate_type('page', 'pages')
20
+ 'pages'
21
+ when 'post'
22
+ Deprecator.defaults_deprecate_type('post', 'posts')
23
+ 'posts'
24
+ when 'draft'
25
+ Deprecator.defaults_deprecate_type('draft', 'drafts')
26
+ 'drafts'
27
+ else
28
+ set['scope']['type']
29
+ end
30
+
31
+ set
32
+ end
33
+
34
+ def ensure_time!(set)
35
+ return set unless set.key?('values') && set['values'].key?('date')
36
+ return set if set['values']['date'].is_a?(Time)
37
+ set['values']['date'] = Utils.parse_date(set['values']['date'], "An invalid date format was found in a front-matter default set: #{set}")
38
+ set
39
+ end
40
+
41
+ # Finds a default value for a given setting, filtered by path and type
42
+ #
43
+ # path - the path (relative to the source) of the page, post or :draft the default is used in
44
+ # type - a symbol indicating whether a :page, a :post or a :draft calls this method
45
+ #
46
+ # Returns the default value or nil if none was found
47
+ def find(path, type, setting)
48
+ value = nil
49
+ old_scope = nil
50
+
51
+ matching_sets(path, type).each do |set|
52
+ if set['values'].key?(setting) && has_precedence?(old_scope, set['scope'])
53
+ value = set['values'][setting]
54
+ old_scope = set['scope']
55
+ end
56
+ end
57
+ value
58
+ end
59
+
60
+ # Collects a hash with all default values for a page or post
61
+ #
62
+ # path - the relative path of the page or post
63
+ # type - a symbol indicating the type (:post, :page or :draft)
64
+ #
65
+ # Returns a hash with all default values (an empty hash if there are none)
66
+ def all(path, type)
67
+ defaults = {}
68
+ old_scope = nil
69
+ matching_sets(path, type).each do |set|
70
+ if has_precedence?(old_scope, set['scope'])
71
+ defaults = Utils.deep_merge_hashes(defaults, set['values'])
72
+ old_scope = set['scope']
73
+ else
74
+ defaults = Utils.deep_merge_hashes(set['values'], defaults)
75
+ end
76
+ end
77
+ defaults
78
+ end
79
+
80
+ private
81
+
82
+ # Checks if a given default setting scope matches the given path and type
83
+ #
84
+ # scope - the hash indicating the scope, as defined in _config.yml
85
+ # path - the path to check for
86
+ # type - the type (:post, :page or :draft) to check for
87
+ #
88
+ # Returns true if the scope applies to the given path and type
89
+ def applies?(scope, path, type)
90
+ applies_path?(scope, path) && applies_type?(scope, type)
91
+ end
92
+
93
+ def applies_path?(scope, path)
94
+ return true if !scope.key?('path') || scope['path'].empty?
95
+
96
+ scope_path = Pathname.new(scope['path'])
97
+ Pathname.new(sanitize_path(path)).ascend do |path|
98
+ if path.to_s == scope_path.to_s
99
+ return true
100
+ end
101
+ end
102
+ end
103
+
104
+ # Determines whether the scope applies to type.
105
+ # The scope applies to the type if:
106
+ # 1. no 'type' is specified
107
+ # 2. the 'type' in the scope is the same as the type asked about
108
+ #
109
+ # scope - the Hash defaults set being asked about application
110
+ # type - the type of the document being processed / asked about
111
+ # its defaults.
112
+ #
113
+ # Returns true if either of the above conditions are satisfied,
114
+ # otherwise returns false
115
+ def applies_type?(scope, type)
116
+ !scope.key?('type') || scope['type'].eql?(type.to_s)
117
+ end
118
+
119
+ # Checks if a given set of default values is valid
120
+ #
121
+ # set - the default value hash, as defined in _config.yml
122
+ #
123
+ # Returns true if the set is valid and can be used in this class
124
+ def valid?(set)
125
+ set.is_a?(Hash) && set['values'].is_a?(Hash)
126
+ end
127
+
128
+ # Determines if a new scope has precedence over an old one
129
+ #
130
+ # old_scope - the old scope hash, or nil if there's none
131
+ # new_scope - the new scope hash
132
+ #
133
+ # Returns true if the new scope has precedence over the older
134
+ def has_precedence?(old_scope, new_scope)
135
+ return true if old_scope.nil?
136
+
137
+ new_path = sanitize_path(new_scope['path'])
138
+ old_path = sanitize_path(old_scope['path'])
139
+
140
+ if new_path.length != old_path.length
141
+ new_path.length >= old_path.length
142
+ elsif new_scope.key? 'type'
143
+ true
144
+ else
145
+ !old_scope.key? 'type'
146
+ end
147
+ end
148
+
149
+ # Collects a list of sets that match the given path and type
150
+ #
151
+ # Returns an array of hashes
152
+ def matching_sets(path, type)
153
+ valid_sets.select do |set|
154
+ !set.key?('scope') || applies?(set['scope'], path, type)
155
+ end
156
+ end
157
+
158
+ # Returns a list of valid sets
159
+ #
160
+ # This is not cached to allow plugins to modify the configuration
161
+ # and have their changes take effect
162
+ #
163
+ # Returns an array of hashes
164
+ def valid_sets
165
+ sets = @site.config['defaults']
166
+ return [] unless sets.is_a?(Array)
167
+
168
+ sets.map do |set|
169
+ if valid?(set)
170
+ ensure_time!(update_deprecated_types(set))
171
+ else
172
+ Bunto.logger.warn "Defaults:", "An invalid front-matter default set was found:"
173
+ Bunto.logger.warn "#{set}"
174
+ nil
175
+ end
176
+ end.compact
177
+ end
178
+
179
+ # Sanitizes the given path by removing a leading and adding a trailing slash
180
+ def sanitize_path(path)
181
+ if path.nil? || path.empty?
182
+ ""
183
+ else
184
+ path.gsub(/\A\//, '').gsub(/([^\/])\z/, '\1')
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,3 @@
1
+ module Bunto
2
+ Generator = Class.new(Plugin)
3
+ end
@@ -0,0 +1,101 @@
1
+ module Bunto
2
+ module Hooks
3
+ DEFAULT_PRIORITY = 20
4
+
5
+ # compatibility layer for octopress-hooks users
6
+ PRIORITY_MAP = {
7
+ :low => 10,
8
+ :normal => 20,
9
+ :high => 30
10
+ }.freeze
11
+
12
+ # initial empty hooks
13
+ @registry = {
14
+ :site => {
15
+ :after_reset => [],
16
+ :post_read => [],
17
+ :pre_render => [],
18
+ :post_render => [],
19
+ :post_write => []
20
+ },
21
+ :pages => {
22
+ :post_init => [],
23
+ :pre_render => [],
24
+ :post_render => [],
25
+ :post_write => []
26
+ },
27
+ :posts => {
28
+ :post_init => [],
29
+ :pre_render => [],
30
+ :post_render => [],
31
+ :post_write => []
32
+ },
33
+ :documents => {
34
+ :post_init => [],
35
+ :pre_render => [],
36
+ :post_render => [],
37
+ :post_write => []
38
+ }
39
+ }
40
+
41
+ # map of all hooks and their priorities
42
+ @hook_priority = {}
43
+
44
+ NotAvailable = Class.new(RuntimeError)
45
+ Uncallable = Class.new(RuntimeError)
46
+
47
+ # register hook(s) to be called later, public API
48
+ def self.register(owners, event, priority: DEFAULT_PRIORITY, &block)
49
+ Array(owners).each do |owner|
50
+ register_one(owner, event, priority_value(priority), &block)
51
+ end
52
+ end
53
+
54
+ # Ensure the priority is a Fixnum
55
+ def self.priority_value(priority)
56
+ return priority if priority.is_a?(Fixnum)
57
+ PRIORITY_MAP[priority] || DEFAULT_PRIORITY
58
+ end
59
+
60
+ # register a single hook to be called later, internal API
61
+ def self.register_one(owner, event, priority, &block)
62
+ @registry[owner] ||={
63
+ :post_init => [],
64
+ :pre_render => [],
65
+ :post_render => [],
66
+ :post_write => []
67
+ }
68
+
69
+ unless @registry[owner][event]
70
+ raise NotAvailable, "Invalid hook. #{owner} supports only the " \
71
+ "following hooks #{@registry[owner].keys.inspect}"
72
+ end
73
+
74
+ unless block.respond_to? :call
75
+ raise Uncallable, "Hooks must respond to :call"
76
+ end
77
+
78
+ insert_hook owner, event, priority, &block
79
+ end
80
+
81
+ def self.insert_hook(owner, event, priority, &block)
82
+ @hook_priority[block] = "#{priority}.#{@hook_priority.size}".to_f
83
+ @registry[owner][event] << block
84
+ end
85
+
86
+ # interface for Bunto core components to trigger hooks
87
+ def self.trigger(owner, event, *args)
88
+ # proceed only if there are hooks to call
89
+ return unless @registry[owner]
90
+ return unless @registry[owner][event]
91
+
92
+ # hooks to call for this owner and event
93
+ hooks = @registry[owner][event]
94
+
95
+ # sort and call hooks according to priority and load order
96
+ hooks.sort_by { |h| @hook_priority[h] }.each do |hook|
97
+ hook.call(*args)
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,49 @@
1
+ module Bunto
2
+ class Layout
3
+ include Convertible
4
+
5
+ # Gets the Site object.
6
+ attr_reader :site
7
+
8
+ # Gets the name of this layout.
9
+ attr_reader :name
10
+
11
+ # Gets the path to this layout.
12
+ attr_reader :path
13
+
14
+ # Gets/Sets the extension of this layout.
15
+ attr_accessor :ext
16
+
17
+ # Gets/Sets the Hash that holds the metadata for this layout.
18
+ attr_accessor :data
19
+
20
+ # Gets/Sets the content of this layout.
21
+ attr_accessor :content
22
+
23
+ # Initialize a new Layout.
24
+ #
25
+ # site - The Site.
26
+ # base - The String path to the source.
27
+ # name - The String filename of the post file.
28
+ def initialize(site, base, name)
29
+ @site = site
30
+ @base = base
31
+ @name = name
32
+ @path = site.in_source_dir(base, name)
33
+
34
+ self.data = {}
35
+
36
+ process(name)
37
+ read_yaml(base, name)
38
+ end
39
+
40
+ # Extract information from the layout filename.
41
+ #
42
+ # name - The String filename of the layout file.
43
+ #
44
+ # Returns nothing.
45
+ def process(name)
46
+ self.ext = File.extname(name)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,22 @@
1
+ module Bunto
2
+ module LiquidExtensions
3
+
4
+ # Lookup a Liquid variable in the given context.
5
+ #
6
+ # context - the Liquid context in question.
7
+ # variable - the variable name, as a string.
8
+ #
9
+ # Returns the value of the variable in the context
10
+ # or the variable name if not found.
11
+ def lookup_variable(context, variable)
12
+ lookup = context
13
+
14
+ variable.split(".").each do |value|
15
+ lookup = lookup[value]
16
+ end
17
+
18
+ lookup || variable
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,39 @@
1
+ require 'bunto/liquid_renderer/file'
2
+ require 'bunto/liquid_renderer/table'
3
+
4
+ module Bunto
5
+ class LiquidRenderer
6
+ def initialize(site)
7
+ @site = site
8
+ reset
9
+ end
10
+
11
+ def reset
12
+ @stats = {}
13
+ end
14
+
15
+ def file(filename)
16
+ filename = @site.in_source_dir(filename).sub(/\A#{Regexp.escape(@site.source)}\//, '')
17
+
18
+ LiquidRenderer::File.new(self, filename).tap do
19
+ @stats[filename] ||= {}
20
+ @stats[filename][:count] ||= 0
21
+ @stats[filename][:count] += 1
22
+ end
23
+ end
24
+
25
+ def increment_bytes(filename, bytes)
26
+ @stats[filename][:bytes] ||= 0
27
+ @stats[filename][:bytes] += bytes
28
+ end
29
+
30
+ def increment_time(filename, time)
31
+ @stats[filename][:time] ||= 0.0
32
+ @stats[filename][:time] += time
33
+ end
34
+
35
+ def stats_table(n = 50)
36
+ LiquidRenderer::Table.new(@stats).to_s(n)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,50 @@
1
+ module Bunto
2
+ class LiquidRenderer
3
+ class File
4
+ def initialize(renderer, filename)
5
+ @renderer = renderer
6
+ @filename = filename
7
+ end
8
+
9
+ def parse(content)
10
+ measure_time do
11
+ @template = Liquid::Template.parse(content, line_numbers: true)
12
+ end
13
+
14
+ self
15
+ end
16
+
17
+ def render(*args)
18
+ measure_time do
19
+ measure_bytes do
20
+ @template.render(*args)
21
+ end
22
+ end
23
+ end
24
+
25
+ def render!(*args)
26
+ measure_time do
27
+ measure_bytes do
28
+ @template.render!(*args)
29
+ end
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def measure_bytes
36
+ yield.tap do |str|
37
+ @renderer.increment_bytes(@filename, str.bytesize)
38
+ end
39
+ end
40
+
41
+ def measure_time
42
+ before = Time.now
43
+ yield
44
+ ensure
45
+ after = Time.now
46
+ @renderer.increment_time(@filename, after - before)
47
+ end
48
+ end
49
+ end
50
+ end