edison 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README.md +59 -0
  2. data/bin/edison +7 -201
  3. data/lib/edison.rb +222 -0
  4. metadata +5 -4
data/README.md CHANGED
@@ -0,0 +1,59 @@
1
+ # Edison
2
+
3
+ Jekyll is nice, but doesn't give you much flexibility. Everything is a post! What if you want a static site with multiple types of objects?
4
+
5
+ Enter Edison.
6
+
7
+ ```
8
+ $ cat <<END > _config.rb
9
+ Edison.config do
10
+ models.posts.each do |post|
11
+ Edison::Helpers.date_from_filename(post)
12
+ post.url = "posts/#{post._fname}"
13
+ routes.url File.join(post.url, "index.html"), "post", post
14
+ end
15
+
16
+ routes.url "index.html" do |data|
17
+ data.posts = models.posts
18
+ end
19
+ end
20
+ END
21
+
22
+ $ mkdir -p _models/posts
23
+ $ cat <<END > _models/posts/$(date +%Y-%m-%d)-my-first-post.md
24
+ ---
25
+ title: My first post!
26
+ ---
27
+ I'm blogging on [Edison](http://github.com/michaelmaltese/edison)!
28
+ END
29
+
30
+ $ mkdir _views
31
+ $ cat <<END > _views/post.html
32
+ <h1>{{ title }}</h1>
33
+ <em>{{ date }}</em>
34
+ {{{ body }}}
35
+ END
36
+
37
+ $ cat <<END > index.html
38
+ <h1>An Edison Blog</h1>
39
+ <ul>
40
+ {{# posts}}
41
+ <li><a href="{{ url }}">{{ title }}</a></li>
42
+ {{/ posts}}
43
+ </ul>
44
+ END
45
+
46
+ $ edison serve
47
+ Running in /Users/michaelmaltese/edison-example...
48
+ Loading model posts/2013-03-17-my-first-post.md...
49
+ Loading view _views/post.html...
50
+ Copying static files...
51
+ Creating /index.html...
52
+ Creating /posts/2013-03-17-my-first-post/index.html...
53
+ Done!
54
+ [2013-03-17 17:36:47] INFO WEBrick 1.3.1
55
+ [2013-03-17 17:36:47] INFO ruby 1.8.7 (2012-02-08) [universal-darwin11.0]
56
+ [2013-03-17 17:36:48] INFO WEBrick::HTTPServer#start: pid=28835 port=4000
57
+ ```
58
+
59
+ Mustache, Markdown, and YAML keep you nice and clean.
data/bin/edison CHANGED
@@ -1,206 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'fileutils'
3
+ require 'commander/import'
4
+ require 'edison'
4
5
  require 'listen'
5
- require 'kramdown'
6
- require 'hashie'
7
- require 'mustache'
8
6
  require 'pp'
9
7
  require 'webrick'
10
- require 'yaml'
11
-
12
- module Edison
13
- end
14
-
15
- module Edison::YAMLFrontMatter
16
- def self.read(fname)
17
- contents = File.read(fname).strip
18
- if contents =~ /\A---/
19
- _, yaml, body = contents.split("---", 3)
20
- yaml = YAML.load(yaml)
21
- body ||= ""
22
- body.strip!
23
- if yaml.include? "body"
24
- raise Exception, "YAML Front Matter can't contain a 'body' key. Put it after the front matter."
25
- end
26
- {"body" => body}.merge(yaml)
27
- else
28
- {"body" => contents}
29
- end
30
- end
31
- end
32
-
33
- class Edison::ModelsLoader
34
- def load(directory)
35
- klasses = Dir[File.join(directory, "*")].map &File.method(:basename)
36
- hash = Hashie::Mash.new
37
- klasses.each do |klass|
38
- objects = Dir[File.join(directory, klass, "*")]
39
- objects.map! do |fname|
40
- puts "Loading model #{File.join(klass, File.basename(fname))}..."
41
-
42
- data = Hashie::Mash.new(case File.extname(fname)
43
- when /(html)|(md)$/
44
- Edison::YAMLFrontMatter.read(fname)
45
- when /ya?ml$/
46
- YAML.load_file(fname)
47
- else
48
- {}
49
- end)
50
-
51
- if data._ext
52
- raise Exception, "Restricted key '_ext' appears in model!"
53
- end
54
- data._ext = File.extname(fname)
55
-
56
- if data._ext == ".md"
57
- data.body = Kramdown::Document.new(data.body).to_html
58
- end
59
-
60
- if data._fname
61
- raise Exception, "Restricted key '_fname' appears in model!"
62
- end
63
- data._fname = File.basename(fname).sub(/#{File.extname(fname)}$/,'')
64
-
65
- data
66
- end
67
- hash[klass] = objects
68
- end
69
- hash
70
- end
71
- end
72
-
73
- class Edison::Renderer
74
- attr_accessor :views
75
- def initialize(views)
76
- self.views = views
77
- end
78
- def render(view_name, data)
79
- view = self.views[view_name]
80
- if view.nil?
81
- raise Exception, "Called for view #{view_name}, but _views/#{view_name} does not exist"
82
- end
83
- body = Mustache.render(view.body, data)
84
- if view.layout
85
- newdata = data.merge("yield" => body)
86
- self.render(view.layout, newdata)
87
- else
88
- body
89
- end
90
- end
91
- end
92
-
93
- class Edison::Router
94
- attr_reader :urls, :static
95
- def initialize
96
- @urls = []
97
- @static = []
98
- end
99
- def url(url, view=nil, data={}, &b)
100
- if b and view and data
101
- raise Exception, "Please pass data or block, not both!"
102
- end
103
- if b
104
- b.call(@urls.find { |(url, _, _)| url == url}[2])
105
- else
106
- @urls << [url, view, Hashie::Mash.new(data)]
107
- end
108
- end
109
- def copy(url)
110
- @static << url
111
- end
112
- end
113
-
114
- module Edison
115
- class <<self
116
- attr_reader :models, :routes, :renderer
117
- end
118
- def self.initialize!(directory)
119
- @directory = directory
120
-
121
- @routes = Router.new
122
-
123
- loader = ModelsLoader.new
124
- @models = loader.load(File.join(directory, "_models"))
125
-
126
- views = Hash[Dir[File.join(directory, "_views", "*")].map do |fname|
127
- puts "Loading view _views/#{File.basename(fname)}..."
128
- name = File.basename(fname).sub(/\.[^\.]+$/,'')
129
- data = Hashie::Mash.new Edison::YAMLFrontMatter.read(fname)
130
- [name, data]
131
- end]
132
-
133
- static = Dir[File.join(directory, "**/*")]
134
- blacklist = ["Gemfile", "Gemfile.lock", "config.rb", "gen.rb", "serve.rb"]
135
- static.reject! do |fname|
136
- name = fname.sub(/^#{directory}\//,'')
137
- dirname = File.dirname(fname)
138
- blacklist.include?(name) or name =~ /^_/ or File.directory?(fname)
139
- end
140
- static.each do |fname|
141
- name = fname.sub(/^#{directory}\//,'')
142
- if %w{.html .md}.include?(File.extname(fname))
143
- data = Hashie::Mash.new Edison::YAMLFrontMatter.read(fname)
144
- url = name.sub(/\.md$/,'')
145
- views[url] = data
146
- @routes.url url, url, data
147
- else
148
- @routes.copy name
149
- end
150
- end
151
-
152
- @renderer = Renderer.new(views)
153
- end
154
- def self.config(&b)
155
- instance_eval &b
156
- end
157
- def self.generate!
158
- site = File.join(@directory, "_site")
159
-
160
- FileUtils.rm_rf(site)
161
- Dir.mkdir(site)
162
- puts "Copying static files..."
163
- routes.static.each do |name|
164
- src = File.join(@directory, name)
165
- dest = File.join(site, name)
166
- FileUtils.mkdir_p File.dirname(dest)
167
- FileUtils.copy src, dest
168
- end
169
- routes.urls.each do |(url, view_name, data)|
170
- puts "Creating /#{url}..."
171
-
172
- fname = File.join(site, url)
173
- FileUtils.mkdir_p(File.dirname(fname))
174
- File.open(fname, "w") do |f|
175
- f.write @renderer.render(view_name, data)
176
- end
177
- end
178
- end
179
- end
180
-
181
- module Edison::Helpers
182
- def self.date_from_filename(data)
183
- if data.date
184
- raise Exception, "Date will be inferred from filename, but found in data"
185
- end
186
-
187
- if File.basename(data._fname) =~ /^(\d{4}-\d{1,2}-\d{1,2})/
188
- data.date = Date.parse($1)
189
- else
190
- raise Exception, "Expected filename to start with date (YYYY-M?M-D?D)"
191
- end
192
-
193
- data
194
- end
195
-
196
- def self.default_layout(data)
197
- unless data.layout
198
- data.layout = "default"
199
- end
200
-
201
- data
202
- end
203
- end
204
8
 
205
9
  def build
206
10
  directory = Dir.pwd
@@ -212,10 +16,10 @@ def build
212
16
  puts "Done!"
213
17
  end
214
18
 
215
- require 'commander/import'
19
+ puts "Edison #{Edison::VERSION} on Ruby #{RUBY_VERSION}"
216
20
 
217
21
  program :name, "Edison"
218
- program :version, "0.0.1"
22
+ program :version, Edison::VERSION
219
23
  program :description, "Static website generator"
220
24
 
221
25
  default_command :help
@@ -251,12 +55,14 @@ command :serve do |c|
251
55
  pp e
252
56
  end
253
57
  end
254
- listener.start(false)
255
58
 
256
59
  Signal.trap("INT") do
257
60
  server.shutdown
258
61
  listener.stop
62
+ exit
63
+ Kernel.abort
259
64
  end
65
+ listener.start(false)
260
66
  server.start
261
67
  end
262
68
  end
@@ -0,0 +1,222 @@
1
+ require 'fileutils'
2
+ require 'kramdown'
3
+ require 'hashie'
4
+ require 'mustache'
5
+ require 'ostruct'
6
+ require 'yaml'
7
+
8
+ module Edison
9
+ VERSION = "0.0.3"
10
+ end
11
+
12
+ module Edison::YAMLFrontMatter
13
+ def self.read(fname)
14
+ contents = File.read(fname).strip
15
+ if contents =~ /\A---/
16
+ _, yaml, body = contents.split("---", 3)
17
+ yaml = YAML.load(yaml)
18
+ body ||= ""
19
+ body.strip!
20
+ if yaml.include? "body"
21
+ raise Exception, "YAML Front Matter can't contain a 'body' key. Put it after the front matter."
22
+ end
23
+ {"body" => body}.merge(yaml)
24
+ else
25
+ {"body" => contents}
26
+ end
27
+ end
28
+ end
29
+
30
+ class Edison::ModelsLoader
31
+ def load(directory)
32
+ klasses = Dir[File.join(directory, "*")].map &File.method(:basename)
33
+ hash = Hashie::Mash.new
34
+ klasses.each do |klass|
35
+ objects = Dir[File.join(directory, klass, "*")]
36
+ objects.map! do |fname|
37
+ puts "Loading model #{File.join(klass, File.basename(fname))}..."
38
+
39
+ data = Hashie::Mash.new(case File.extname(fname)
40
+ when /(html)|(md)$/
41
+ Edison::YAMLFrontMatter.read(fname)
42
+ when /ya?ml$/
43
+ YAML.load_file(fname)
44
+ else
45
+ {}
46
+ end)
47
+
48
+ if data._ext
49
+ raise Exception, "Restricted key '_ext' appears in model!"
50
+ end
51
+ data._ext = File.extname(fname)
52
+
53
+ if data._ext == ".md"
54
+ data.body = Kramdown::Document.new(data.body).to_html
55
+ end
56
+
57
+ if data._fname
58
+ raise Exception, "Restricted key '_fname' appears in model!"
59
+ end
60
+ data._fname = File.basename(fname).sub(/#{File.extname(fname)}$/,'')
61
+
62
+ data
63
+ end
64
+ hash[klass] = objects
65
+ end
66
+ hash
67
+ end
68
+ end
69
+
70
+ class Edison::Renderer
71
+ attr_accessor :views
72
+ def initialize(views)
73
+ self.views = views
74
+ end
75
+ def render(view_name, data)
76
+ view = self.views[view_name]
77
+ if view.nil?
78
+ raise Exception, "Called for view #{view_name}, but _views/#{view_name} does not exist"
79
+ end
80
+ body = Mustache.render(view.body, data)
81
+ if view.layout
82
+ newdata = data.merge("yield" => body)
83
+ self.render(view.layout, newdata)
84
+ else
85
+ body
86
+ end
87
+ end
88
+ end
89
+
90
+ class Edison::Router
91
+ attr_reader :urls, :static
92
+ def initialize
93
+ @urls = []
94
+ @static = []
95
+ end
96
+ def url(url, view=nil, data={}, &b)
97
+ if b and view and data
98
+ raise Exception, "Please pass data or block, not both!"
99
+ end
100
+ if b
101
+ data = @urls.find { |(u, _, _)| u == url}[2]
102
+ b.call(data)
103
+ else
104
+ @urls << [url, view, Hashie::Mash.new(data)]
105
+ end
106
+ end
107
+ def copy(url)
108
+ @static << url
109
+ end
110
+ end
111
+
112
+ class Edison::StaticFileHandler
113
+ def self.filter_blacklisted(directory, files)
114
+ blacklist = ["Gemfile", "Gemfile.lock", "config.rb", "Rakefile", "Procfile", "Makefile"]
115
+ files.reject do |fname|
116
+ name = fname.sub(/^#{directory}\//,'')
117
+ dirname = File.dirname(fname)
118
+ blacklist.include?(name) or name =~ /^_/ or File.directory?(fname)
119
+ end
120
+ end
121
+ def self.load(directory)
122
+ static = Dir[File.join(directory, "**/*")]
123
+ static = self.filter_blacklisted(directory, static)
124
+
125
+ possibly_dynamic = []
126
+ just_copy = []
127
+
128
+ static.each do |fname|
129
+ name = fname.sub(/^#{directory}\//,'')
130
+ if %w{.html .md}.include?(File.extname(fname))
131
+ data = Hashie::Mash.new Edison::YAMLFrontMatter.read(fname)
132
+ url = name.sub(/\.md$/,'')
133
+ possibly_dynamic << [url, data]
134
+ else
135
+ just_copy << name
136
+ end
137
+ end
138
+
139
+ OpenStruct.new(
140
+ :possibly_dynamic => possibly_dynamic,
141
+ :just_copy => just_copy
142
+ )
143
+ end
144
+ end
145
+
146
+ module Edison
147
+ class <<self
148
+ attr_reader :models, :routes, :renderer
149
+ end
150
+ def self.initialize!(directory)
151
+ @directory = directory
152
+
153
+ @routes = Router.new
154
+
155
+ loader = ModelsLoader.new
156
+ @models = loader.load(File.join(directory, "_models"))
157
+
158
+ @views = Hash[Dir[File.join(directory, "_views", "*")].map do |fname|
159
+ puts "Loading view _views/#{File.basename(fname)}..."
160
+ name = File.basename(fname).sub(/\.[^\.]+$/,'')
161
+ data = Hashie::Mash.new Edison::YAMLFrontMatter.read(fname)
162
+ [name, data]
163
+ end]
164
+
165
+ static = StaticFileHandler.load(directory)
166
+ static.possibly_dynamic.each do |(url, data)|
167
+ @routes.url url, url, data
168
+ @views[url] = data
169
+ end
170
+ static.just_copy.each &@routes.method(:copy)
171
+ end
172
+ def self.config(&b)
173
+ instance_eval &b
174
+ end
175
+ def self.generate!
176
+ renderer = Renderer.new(@views)
177
+ site = File.join(@directory, "_site")
178
+
179
+ FileUtils.rm_rf(site)
180
+ Dir.mkdir(site)
181
+ puts "Copying static files..."
182
+ routes.static.each do |name|
183
+ src = File.join(@directory, name)
184
+ dest = File.join(site, name)
185
+ FileUtils.mkdir_p File.dirname(dest)
186
+ FileUtils.copy src, dest
187
+ end
188
+ routes.urls.each do |(url, view_name, data)|
189
+ puts "Creating /#{url}..."
190
+
191
+ fname = File.join(site, url)
192
+ FileUtils.mkdir_p(File.dirname(fname))
193
+ File.open(fname, "w") do |f|
194
+ f.write renderer.render(view_name, data)
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ module Edison::Helpers
201
+ def self.date_from_filename(data)
202
+ if data.date
203
+ raise Exception, "Date will be inferred from filename, but found in data"
204
+ end
205
+
206
+ if File.basename(data._fname) =~ /^(\d{4}-\d{1,2}-\d{1,2})/
207
+ data.date = Date.parse($1)
208
+ else
209
+ raise Exception, "Expected filename to start with date (YYYY-M?M-D?D)"
210
+ end
211
+
212
+ data
213
+ end
214
+
215
+ def self.default_layout(data)
216
+ unless data.layout
217
+ data.layout = "default"
218
+ end
219
+
220
+ data
221
+ end
222
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: edison
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 3
10
+ version: 0.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Michael Maltese
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2013-03-17 00:00:00 Z
18
+ date: 2013-03-23 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: commander
@@ -96,6 +96,7 @@ extensions: []
96
96
  extra_rdoc_files: []
97
97
 
98
98
  files:
99
+ - lib/edison.rb
99
100
  - bin/edison
100
101
  - README.md
101
102
  homepage: http://github.com/michaelmaltese/edison