dimples 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9af69955701e46818367fbe965f4bb1071427319
4
+ data.tar.gz: 2d2c2861d73eef0348bb98f7d06ec6e94c3b007e
5
+ SHA512:
6
+ metadata.gz: 422c790ef28519c67b634dffe74515cf52f6941e62bff67180822f26cb95c6f89be25608a9c73761a65812d4efbb4e0e80c1e34976bad0dac20d5dbc3d39442e
7
+ data.tar.gz: d30324038e2fad5432ca578ba84f7936fe961c2e30885a473893dfa2844bb012a9b055ede5bf8d48958de458e20fbe3ff089d1adbed049637774db0a216183a0
data/lib/dimples.rb ADDED
@@ -0,0 +1,14 @@
1
+ $LOAD_PATH.unshift(__dir__)
2
+
3
+ require 'fileutils'
4
+ require 'yaml'
5
+ require 'erubis'
6
+ require 'redcarpet'
7
+
8
+ require 'dimples/frontable'
9
+ require 'dimples/publishable'
10
+ require 'dimples/configuration'
11
+ require 'dimples/page'
12
+ require 'dimples/post'
13
+ require 'dimples/site'
14
+ require 'dimples/template'
@@ -0,0 +1,74 @@
1
+ module Dimples
2
+ class Configuration
3
+ def initialize(config = {})
4
+ @settings = Dimples::Configuration.default_settings
5
+
6
+ if config
7
+ @settings.each_key do |key|
8
+ if config.key?(key)
9
+ if @settings[key].is_a?(Hash)
10
+ @settings[key].merge!(config[key])
11
+ else
12
+ @settings[key] = config[key]
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ def [](key)
20
+ @settings[key]
21
+ end
22
+
23
+ def self.default_settings
24
+ current_path = Dir.pwd
25
+
26
+ {
27
+ 'source_path' => current_path,
28
+ 'destination_path' => File.join(current_path, 'site'),
29
+
30
+ 'paths' => {
31
+ 'posts' => 'archives',
32
+ 'post' => '%Y/%m/%d',
33
+ },
34
+
35
+ 'layouts' => {
36
+ 'posts' => 'posts',
37
+ 'post' => 'post',
38
+ 'category' => 'category',
39
+ },
40
+
41
+ 'markdown' => {
42
+ 'enabled' => true,
43
+ 'options' => {},
44
+ },
45
+
46
+ 'pagination' => {
47
+ 'enabled' => true,
48
+ 'per_page' => 10,
49
+ },
50
+
51
+ 'generation' => {
52
+ 'paginated_posts' => false,
53
+ 'categories' => true,
54
+ 'year_archives' => true,
55
+ 'month_archives' => true,
56
+ 'day_archives' => true,
57
+ 'feed' => true,
58
+ 'category_feeds' => true,
59
+ },
60
+
61
+ 'file_extensions' => {
62
+ 'pages' => 'html',
63
+ 'posts' => 'html',
64
+ },
65
+
66
+ 'date_formats' => {
67
+ 'year' => '%Y',
68
+ 'month' => '%Y-%m',
69
+ 'day' => '%Y-%m-%d',
70
+ }
71
+ }
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,29 @@
1
+ module Dimples
2
+ module Frontable
3
+ def read_with_yaml(path)
4
+ if path =~ /.yml$/
5
+ contents = ''
6
+ metadata = YAML.load_file(path)
7
+ else
8
+ contents = File.read(path)
9
+ matches = contents.match(/^(-{3}\n.*?\n?)^(-{3}*$\n?)/m)
10
+
11
+ if matches
12
+ metadata = YAML.load(matches[1])
13
+ contents = matches.post_match.strip!
14
+ end
15
+ end
16
+
17
+ if metadata
18
+ metadata.each_pair do |key, value|
19
+ unless instance_variable_get("@#{key}")
20
+ self.class.send(:attr_accessor, key.to_sym)
21
+ instance_variable_set("@#{key}", value)
22
+ end
23
+ end
24
+ end
25
+
26
+ contents
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,47 @@
1
+ module Dimples
2
+ class Page
3
+ include Frontable
4
+ include Publishable
5
+
6
+ attr_accessor :path
7
+ attr_accessor :title
8
+ attr_accessor :template
9
+ attr_accessor :filename
10
+ attr_accessor :extension
11
+ attr_accessor :layout
12
+
13
+ attr_writer :contents
14
+
15
+ def initialize(site, path = nil)
16
+ @site = site
17
+
18
+ if path
19
+ @path = path
20
+ @contents = read_with_yaml(path)
21
+ @filename = File.basename(path, File.extname(path))
22
+ else
23
+ @filename = 'index'
24
+ end
25
+
26
+ @extension = @site.config['file_extensions']['pages']
27
+ @layout ||= @site.config['layouts']['page']
28
+ end
29
+
30
+ def type
31
+ :page
32
+ end
33
+
34
+ def contents
35
+ @contents
36
+ end
37
+
38
+ def output_file_path(parent_path)
39
+ parts = [parent_path]
40
+
41
+ parts << File.dirname(@path).gsub(@site.source_paths[:pages], '') unless @path.nil?
42
+ parts << "#{@filename}.#{@extension}"
43
+
44
+ File.join(parts)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,70 @@
1
+ module Dimples
2
+ class Post
3
+ include Frontable
4
+ include Publishable
5
+
6
+ attr_accessor :path
7
+ attr_accessor :title
8
+ attr_accessor :template
9
+ attr_accessor :filename
10
+ attr_accessor :extension
11
+ attr_accessor :layout
12
+ attr_accessor :slug
13
+ attr_accessor :date
14
+ attr_accessor :categories
15
+ attr_accessor :year
16
+ attr_accessor :month
17
+ attr_accessor :day
18
+
19
+ attr_writer :contents
20
+ attr_writer :markdown
21
+
22
+ def initialize(site, path)
23
+ @site = site
24
+
25
+ @path = path
26
+ @contents = read_with_yaml(path)
27
+
28
+ @filename = 'index'
29
+ @extension = @site.config['file_extensions']['posts']
30
+
31
+ parts = File.basename(path, File.extname(path)).match(/(\d{4})-(\d{2})-(\d{2})-(.+)/)
32
+
33
+ @slug = parts[4]
34
+ @date = Time.mktime(parts[1], parts[2], parts[3])
35
+
36
+ @layout ||= @site.config['layouts']['post']
37
+ @categories ||= []
38
+
39
+ @year = @date.strftime('%Y')
40
+ @month = @date.strftime('%m')
41
+ @day = @date.strftime('%d')
42
+ end
43
+
44
+ def type
45
+ :post
46
+ end
47
+
48
+ def contents
49
+ @contents
50
+ end
51
+
52
+ def markdown
53
+ @markdown ||= @site.markdown_engine.render(contents())
54
+ end
55
+
56
+ def output_file_path(parent_path)
57
+ parts = [parent_path]
58
+
59
+ if @site.config['paths']['post']
60
+ path = @date.strftime(@site.config['paths']['post'])
61
+ parts.concat(path.split('/')) if path
62
+ end
63
+
64
+ parts << @slug
65
+ parts << "#{@filename}.#{@extension}"
66
+
67
+ File.join(parts)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,32 @@
1
+ module Dimples
2
+ module Publishable
3
+ def write(path, context = false)
4
+ output = context ? render(contents(), {this: self}.merge(context)) : contents()
5
+
6
+ publish_path = output_file_path(path)
7
+ parent_path = File.dirname(publish_path)
8
+
9
+ FileUtils.mkdir_p(parent_path) unless Dir.exist?(parent_path)
10
+
11
+ File.open(publish_path, 'w') do |file|
12
+ file.write(output)
13
+ end
14
+ end
15
+
16
+ def render(body, context = {}, use_layout = true)
17
+ context[:site] ||= @site
18
+
19
+ begin
20
+ output = Erubis::Eruby.new(contents()).evaluate(context) { body }
21
+ rescue SyntaxError => e
22
+ raise "Syntax error in #{path.gsub(site.source_paths[:root], '')}"
23
+ end
24
+
25
+ if use_layout && @layout && @site.templates[@layout]
26
+ output = @site.templates[@layout].render(output, context)
27
+ end
28
+
29
+ output
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,278 @@
1
+ module Dimples
2
+ class Site
3
+ attr_accessor :source_paths
4
+ attr_accessor :output_paths
5
+ attr_accessor :config
6
+ attr_accessor :templates
7
+ attr_accessor :categories
8
+ attr_accessor :archives
9
+ attr_accessor :pages
10
+ attr_accessor :posts
11
+ attr_accessor :latest_post
12
+ attr_accessor :markdown_engine
13
+ attr_accessor :page_class
14
+ attr_accessor :post_class
15
+
16
+ def initialize(config = {})
17
+ @source_paths = {}
18
+ @output_paths = {}
19
+ @templates = {}
20
+ @categories = {}
21
+ @archives = {}
22
+
23
+ @pages = []
24
+ @posts = []
25
+
26
+ @latest_post = false
27
+
28
+ @page_class = Dimples::Page
29
+ @post_class = Dimples::Post
30
+
31
+ @config = Dimples::Configuration.new(config)
32
+
33
+ @source_paths[:root] = File.expand_path(@config['source_path'])
34
+ @output_paths[:site] = File.expand_path(@config['destination_path'])
35
+
36
+ %w{pages posts templates public}.each do |path|
37
+ @source_paths[path.to_sym] = File.join(@source_paths[:root], path)
38
+ end
39
+
40
+ @output_paths[:posts] = File.join(@output_paths[:site], @config['paths']['posts'])
41
+
42
+ @markdown_engine = if @config['markdown']['enabled']
43
+ Redcarpet::Markdown.new(Redcarpet::Render::HTML, @config['markdown']['options'])
44
+ else
45
+ false
46
+ end
47
+ end
48
+
49
+ def scan_files
50
+ Dir.glob(File.join(@source_paths[:templates], '**', '*.*')).each do |path|
51
+ template = Dimples::Template.new(self, path)
52
+ @templates[template.slug] = template
53
+ end
54
+
55
+ Dir.glob(File.join(@source_paths[:pages], '**', '*.*')).each do |path|
56
+ @pages << @page_class.new(self, path)
57
+ end
58
+
59
+ Dir.glob(File.join(@source_paths[:posts], '*.*')).each do |path|
60
+ @posts << @post_class.new(self, path)
61
+ end
62
+
63
+ @posts.reverse!
64
+ @latest_post = @posts.first
65
+
66
+ @posts.each do |post|
67
+
68
+ year = post.year.to_s
69
+ month = post.month.to_s
70
+ day = post.day.to_s
71
+
72
+ @archives[year] ||= {posts: [], months: {}}
73
+
74
+ @archives[year][:posts] << post
75
+ @archives[year][:months][month] ||= {posts: [], days: {}, name: post.date.strftime('%B')}
76
+ @archives[year][:months][month][:posts] << post
77
+ @archives[year][:months][month][:days][day] ||= []
78
+ @archives[year][:months][month][:days][day] << post
79
+
80
+ post.categories.each do |category|
81
+ (@categories[category] ||= []) << post
82
+ end
83
+ end
84
+ end
85
+
86
+ def generate
87
+ scan_files
88
+
89
+ begin
90
+ FileUtils.remove_dir(@output_paths[:site]) if Dir.exist?(@output_paths[:site])
91
+ Dir.mkdir(@output_paths[:site])
92
+ rescue => e
93
+ raise "Failed to prepare the site directory (#{e})"
94
+ end
95
+
96
+ @posts.each do |post|
97
+ begin
98
+ post.write(@output_paths[:posts], {})
99
+ rescue => e
100
+ raise "Failed to render post #{File.basename(post.path)} (#{e})"
101
+ end
102
+ end
103
+
104
+ @pages.each do |page|
105
+ begin
106
+ page.write(@output_paths[:site], {})
107
+ rescue => e
108
+ raise "Failed to render page from #{page.path.gsub(@source_paths[:root], '')} (#{e})"
109
+ end
110
+ end
111
+
112
+ if @config['generation']['paginated_posts'] && @posts.length > 0
113
+ begin
114
+ paginate(@posts, false, @config['pagination']['per_page'], [@output_paths[:posts]], @config['layouts']['posts'])
115
+ rescue => e
116
+ raise "Failed to paginate main posts (#{e})"
117
+ end
118
+ end
119
+
120
+ if @config['generation']['year_archives'] && @archives.length > 0
121
+ begin
122
+ generate_archives
123
+ rescue => e
124
+ raise "Failed to generate archives (#{e})"
125
+ end
126
+ end
127
+
128
+ if @config['generation']['categories'] && @categories.length > 0
129
+ @categories.each_pair do |slug, posts|
130
+ if @config['generation']['category_feeds']
131
+ begin
132
+ generate_feed(File.join(@output_paths[:posts], slug), {posts: posts[0..@config['pagination']['per_page'] - 1], category: slug})
133
+ rescue => e
134
+ raise "Failed to generate category feed for '#{slug}' (#{e})"
135
+ end
136
+ end
137
+
138
+ begin
139
+ paginate(posts, slug.capitalize, @config['pagination']['per_page'], [@output_paths[:posts], slug], @config['layouts']['category'])
140
+ rescue => e
141
+ raise "Failed to generate category pages for '#{slug}' (#{e})"
142
+ end
143
+ end
144
+ end
145
+
146
+ if @config['generation']['feed'] && @posts.length > 0
147
+ begin
148
+ generate_feed(@output_paths[:site], {posts: @posts[0..@config['pagination']['per_page'] - 1]})
149
+ rescue => e
150
+ raise "Failed to generate main feed (#{e})"
151
+ end
152
+ end
153
+
154
+ begin
155
+ FileUtils.cp_r(File.join(@source_paths[:public], '.'), @output_paths[:site]) if Dir.exists?(@source_paths[:public])
156
+ rescue => e
157
+ raise "Failed to copy site assets (#{e})"
158
+ end
159
+ end
160
+
161
+ def generate_archives
162
+ if @config['layouts'].has_key?('year')
163
+ year_template = @config['layouts']['year']
164
+ elsif @config['layouts'].has_key?('archives')
165
+ year_template = @config['layouts']['archives']
166
+ else
167
+ year_template = @config['layouts']['posts']
168
+ end
169
+
170
+ @archives.each do |year, year_archive|
171
+
172
+ if @config['generation']['month_archives']
173
+ if @config['layouts'].has_key?('month')
174
+ month_template = @config['layouts']['month']
175
+ elsif @config['layouts'].has_key?('archives')
176
+ month_template = @config['layouts']['archives']
177
+ else
178
+ month_template = @config['layouts']['posts']
179
+ end
180
+
181
+ year_archive[:months].each do |month, month_archive|
182
+
183
+ if @config['generation']['day_archives']
184
+
185
+ if @config['layouts'].has_key?('day')
186
+ day_template = @config['layouts']['day']
187
+ elsif @config['layouts'].has_key?('archives')
188
+ day_template = @config['layouts']['archives']
189
+ else
190
+ day_template = @config['layouts']['posts']
191
+ end
192
+
193
+ month_archive[:days].each do |day, posts|
194
+ day_title = posts[0].date.strftime(@config['date_formats']['day'])
195
+ paginate(posts, day_title, @config['pagination']['per_page'], [@output_paths[:posts], year.to_s, month.to_s, day.to_s], day_template)
196
+ end
197
+ end
198
+
199
+ month_title = month_archive[:posts][0].date.strftime(@config['date_formats']['month'])
200
+ paginate(month_archive[:posts], month_title, @config['pagination']['per_page'], [@output_paths[:posts], year.to_s, month.to_s], month_template)
201
+ end
202
+ end
203
+
204
+ year_title = year_archive[:posts][0].date.strftime(@config['date_formats']['year'])
205
+ paginate(year_archive[:posts], year_title, @config['pagination']['per_page'], [@output_paths[:posts], year.to_s], year_template)
206
+ end
207
+ end
208
+
209
+ def generate_feed(path, params)
210
+ feed = @page_class.new(self)
211
+
212
+ feed.filename = 'feed'
213
+ feed.extension = 'atom'
214
+ feed.layout = 'feed'
215
+
216
+ feed.write(path, params)
217
+ end
218
+
219
+ def paginate(posts, title, per_page, paths, layout, params = {})
220
+ fail "'#{layout}' template not found" unless @templates.has_key?(layout)
221
+
222
+ pages = (posts.length.to_f / per_page.to_i).ceil
223
+
224
+ for index in 0...pages
225
+ range = posts.slice(index * per_page, per_page)
226
+
227
+ page = @page_class.new(self)
228
+
229
+ page_paths = paths.clone
230
+ page_title = title ? title : @templates[layout].title
231
+
232
+ if page_paths[0] == @output_paths[:site]
233
+ url_path = '/'
234
+ else
235
+ url_path = "/#{File.split(page_paths[0])[-1]}/"
236
+ end
237
+
238
+ url_path += "#{page_paths[1..-1].join('/')}/" if page_paths.length > 1
239
+
240
+ if index > 0
241
+ page_paths.push("page#{index + 1}")
242
+ end
243
+
244
+ pagination = {
245
+ page: index + 1,
246
+ pages: pages,
247
+ total: posts.length,
248
+ path: url_path
249
+ }
250
+
251
+ if (pagination[:page] - 1) > 0
252
+ pagination[:previous_page] = pagination[:page] - 1
253
+ end
254
+
255
+ if (pagination[:page] + 1) <= pagination[:pages]
256
+ pagination[:next_page] = pagination[:page] + 1
257
+ end
258
+
259
+ page.layout = layout
260
+ page.title = page_title
261
+
262
+ page.write(File.join(page_paths), {posts: range, pagination: pagination}.merge(params))
263
+ end
264
+ end
265
+
266
+ def render(template_slug, content, layout = true)
267
+ return '' unless @templates[template_slug]
268
+
269
+ if content.is_a? String
270
+ @templates[template_slug].render(content, {}, layout)
271
+ elsif content.is_a? Hash
272
+ @templates[template_slug].render('', content, layout)
273
+ else
274
+ ''
275
+ end
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,19 @@
1
+ module Dimples
2
+ class Template
3
+ include Frontable
4
+ include Publishable
5
+
6
+ attr_accessor :slug
7
+ attr_accessor :title
8
+ attr_accessor :path
9
+ attr_accessor :contents
10
+
11
+ def initialize(site, path)
12
+ @site = site
13
+ @slug = File.basename(path, File.extname(path))
14
+ @path = path
15
+
16
+ @contents = read_with_yaml(path)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Dimples
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dimples
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Bogan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: erubis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: redcarpet
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.2'
41
+ description: This is a very simple static site generator, born out of the loins of
42
+ usesthis.com.
43
+ email:
44
+ - d+dimples@waferbaby.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - lib/dimples.rb
50
+ - lib/dimples/configuration.rb
51
+ - lib/dimples/frontable.rb
52
+ - lib/dimples/page.rb
53
+ - lib/dimples/post.rb
54
+ - lib/dimples/publishable.rb
55
+ - lib/dimples/site.rb
56
+ - lib/dimples/template.rb
57
+ - lib/dimples/version.rb
58
+ homepage: http://github.com/waferbaby/dimples
59
+ licenses:
60
+ - LICENSE
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.2.2
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: A very silly static site generator
82
+ test_files: []