edison 0.0.2 → 0.0.3

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 (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