flora 0.1.3 → 0.5.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1924be5519b0ec6c689e7ffd28635492f273595e0a6fdd3d482618911475015
4
- data.tar.gz: f6d24d9cf6c0d3c094805d5686320eddc5759542f595184329e1404abf0a0a79
3
+ metadata.gz: d37209dfce3b7a9f42b1f41f261ab64b404a13cfa07bb1246d36718395fb9aff
4
+ data.tar.gz: 145c6f72a9e6c82abad143f946bde715e200b693d5963f3568485b04622c14c4
5
5
  SHA512:
6
- metadata.gz: ba0c66410d2db67867441765ac14f316ccf6f3c94b71da3d49024ac33b0ad7561e251910032d0db8cbb3ccc59087edd09c7ee9b3756d4a112582bafc341b16d5
7
- data.tar.gz: 3590f10225344dd69fccf934e3c3d68e31dfc336a1f43a86294f35df7c3c71cdfa227ddff87e0e307ced25c57644a34b0ab6b4a93b6487488f2dbaac09ddfbb3
6
+ metadata.gz: 283edf79dedaa559fa4132a16f4fb79f897fb969436cf252b23fa520aa172232ddc22c65665e2886f3390761e31678bef7b244ada7eac9edf6a6b20c5f8151ba
7
+ data.tar.gz: c1e279fa9e7a777bc60bec261c6e7a688f34d88d049865ee639564316ecfb30b1eadf82156ab599bb5f1981125c16841b11fb5b38ec187f1c2680b0d6a2e22ba
data/README.md CHANGED
@@ -1,3 +1,27 @@
1
1
  # Flora
2
2
 
3
3
  A static site generator.
4
+
5
+ ## Quickstart
6
+
7
+ Make a new directory with a Gemfile that looks like this:
8
+
9
+ ```ruby
10
+ source "https://rubygems.org"
11
+
12
+ gem 'flora'
13
+ ```
14
+
15
+ Make a new `index.rb` file that looks like this:
16
+
17
+ ```ruby
18
+ html do
19
+ body do
20
+ h1 do
21
+ 'Hello world!'
22
+ end
23
+ end
24
+ end
25
+ ```
26
+
27
+ Install everything with `bundle install`, and then run `bundle exec flora serve` and open up http://localhost:3000!
data/lib/flora/app.rb CHANGED
@@ -2,12 +2,68 @@ require 'rack'
2
2
 
3
3
  class Flora::App
4
4
 
5
+ # TODO: make this support multiple apps.
6
+ OUT_DIR = '/tmp/flora/'
7
+
8
+
9
+ class Rebuilder
10
+
11
+ def initialize(app, dir, flora)
12
+ @app = app
13
+ @dir = Pathname.new(dir)
14
+ @flora = flora
15
+ @last_built = Time.at(0)
16
+ end
17
+
18
+
19
+ def call(env)
20
+ if should_rebuild?
21
+ @flora.build(OUT_DIR)
22
+ @last_built = Time.now.utc
23
+ # TODO: also trigger a reload in the browser
24
+ end
25
+
26
+ @app.call(env)
27
+ end
28
+
29
+
30
+ private
31
+
32
+ def should_rebuild?
33
+ # TODO: this is probably slow. I'm sure there's an easier kqueue-esq way
34
+ # of doing this.
35
+ @dir.find do |file|
36
+ return true if file.stat.mtime > @last_built
37
+ end
38
+
39
+ false
40
+ end
41
+
42
+ end
43
+
44
+
45
+ class Reloader
46
+
47
+ def initialize(app, flora)
48
+ @app = app
49
+ @flora = flora
50
+ end
51
+
52
+ def call(env)
53
+ @flora.reload_website_code
54
+
55
+ @app.call(env)
56
+ end
57
+
58
+ end
59
+
60
+
5
61
  # UGLY. Is there a better way to do this?
6
62
  class StaticWithoutHtml
7
63
 
8
64
  def initialize(app, dir)
9
65
  @app = app
10
- @dir = dir
66
+ @dir = Pathname.new(dir)
11
67
  end
12
68
 
13
69
  def call(env)
@@ -24,13 +80,14 @@ class Flora::App
24
80
 
25
81
 
26
82
  def self.app(path)
27
- flora = Flora::Engine.new(path, '/tmp/flora/')
28
- # TODO: rebuild every req?
29
- flora.build
83
+ flora = Flora.new(path)
30
84
 
31
85
  Rack::Builder.new do
32
- use StaticWithoutHtml, flora.out_dir
33
- use Rack::Static, urls: [''], root: flora.out_dir.to_s, index: 'index.html'
86
+ use Rebuilder, path, flora
87
+ use Reloader, flora
88
+ use StaticWithoutHtml, OUT_DIR
89
+ use Rack::Static, urls: [''], root: OUT_DIR, index: 'index.html'
90
+
34
91
  map "/" do
35
92
  run ->(env) do
36
93
  [404, {'Content-Type' => 'text/plain'}, ['Page Not Found!']]
data/lib/flora/cli.rb CHANGED
@@ -4,7 +4,7 @@ class Flora::Cli < Thor
4
4
 
5
5
  desc 'build OUT_DIR', 'Build a site'
6
6
  def build(out_dir)
7
- Flora::Engine.new('.', out_dir).build
7
+ Flora.new('.').build(out_dir)
8
8
  end
9
9
 
10
10
 
@@ -1,15 +1,12 @@
1
1
  class Flora::Engine::Config
2
2
 
3
- attr_reader(:plugins)
4
-
5
-
6
- def initialize
7
- @plugins = []
3
+ def initialize(plugin_loader)
4
+ @plugin_loader = plugin_loader
8
5
  end
9
6
 
10
7
 
11
8
  def plugin(mod)
12
- @plugins << mod
9
+ @plugin_loader.load_plugin(mod)
13
10
  end
14
11
 
15
12
  end
@@ -9,7 +9,7 @@ module Flora::Engine::Page::Html
9
9
  fieldset figcaption figure footer form
10
10
  h1 h2 h3 h4 h5 h6 head header hr html i iframe img input ins
11
11
  kbd keygen
12
- label legend li
12
+ label legend li link
13
13
  main map mark menu meter
14
14
  nav
15
15
  object ol optgroup option output
data/lib/flora/engine.rb CHANGED
@@ -1,61 +1,72 @@
1
1
  class Flora::Engine
2
2
 
3
- attr_reader(:out_dir)
3
+ attr_reader(:website_loader)
4
4
 
5
5
 
6
- def initialize(path, out_dir)
6
+ def initialize(path)
7
7
  @path = Pathname.new(path)
8
- @out_dir = Pathname.new(out_dir)
9
- @out_dir.mkdir unless @out_dir.exist?
10
- @config = Config.new
8
+ @config = Config.new(self)
9
+
10
+ @website_loader = Zeitwerk::Loader.new
11
+
12
+ if website_has_supporting_code?
13
+ @website_loader.push_dir(@path.join('lib').to_s)
14
+ end
15
+
16
+ @website_loader.enable_reloading
17
+ @website_loader.setup
11
18
 
12
19
  Kernel.prepend(Flora::Engine::Page::Html)
13
20
  end
14
21
 
15
22
 
16
- def build
17
- with_load_path(@path) do
18
- config_file = @path.join('_config.rb')
19
- @config.instance_eval(config_file.read) if config_file.exist?
23
+ def configure
24
+ config_file = @path.join('_config.rb')
25
+ @config.instance_eval(config_file.read) if config_file.exist?
26
+ end
20
27
 
21
- init_plugins
22
28
 
23
- Page.each(@path) do |page|
24
- layouts = []
25
- page.dir.ascend do |dir|
26
- maybe_layout = dir.join('_layout.rb')
27
- layouts << maybe_layout if maybe_layout.exist?
28
- break if dir == @path
29
- end
29
+ def build(out_dir)
30
+ @out_dir = Pathname.new(out_dir)
31
+ @out_dir.mkdir unless @out_dir.exist?
30
32
 
31
- $flora_added = []
32
- tree = Page::Layout.new(layouts).render do
33
- page.render
34
- end
35
- html = Page::Html.to_html(tree)
33
+ Page.each(@path) do |page|
34
+ layouts = []
35
+ page.dir.ascend do |dir|
36
+ maybe_layout = dir.join('_layout.rb')
37
+ layouts << maybe_layout if maybe_layout.exist?
38
+ break if dir == @path
39
+ end
36
40
 
37
- out_filename = @out_dir.join(page.outname)
38
- out_filename.dirname.mkdir unless out_filename.dirname.exist?
39
- out_filename.write(html)
41
+ $flora_added = []
42
+ tree = Page::Layout.new(layouts).render do
43
+ page.render
40
44
  end
45
+ html = Page::Html.to_html(tree)
46
+
47
+ out_filename = @out_dir.join(page.outname)
48
+ out_filename.dirname.mkdir unless out_filename.dirname.exist?
49
+ out_filename.write(html)
41
50
  end
51
+
52
+ end
53
+
54
+
55
+ def load_plugin(mod)
56
+ self.class.include(mod::EngineMethods) if defined?(mod::EngineMethods)
57
+ Page::RubyPage.include(mod::PageMethods) if defined?(mod::PageMethods)
58
+ Config.include(mod::Config) if defined?(mod::Config)
42
59
  end
43
60
 
44
61
 
45
62
  private
46
63
 
47
- def with_load_path(path)
48
- $LOAD_PATH << path.to_s
49
- yield
50
- $LOAD_PATH.delete(path.to_s)
64
+ def website_has_supporting_code?
65
+ @path.join('lib').exist?
51
66
  end
52
67
 
53
68
 
54
- def init_plugins
55
- @config.plugins.each do |plugin|
56
- Page.include(plugin::Page) if self.class.const_defined?("#{plugin}::Page")
57
- Page::RubyPage.include(plugin::RubyPage) if self.class.const_defined?("#{plugin}::RubyPage")
58
- end
69
+ def setup_website_loader
59
70
  end
60
71
 
61
72
  end
@@ -1,18 +1,19 @@
1
- class FloraBlog::Post
1
+ class Flora::Plugins::Blog::Post
2
2
 
3
- def initialize(page)
3
+ def initialize(page, root)
4
4
  @page = page
5
+ @root = root
5
6
  @frontmatter = parse_frontmatter
6
7
  end
7
8
 
8
9
 
9
10
  def url
10
- 'https://google.com'
11
+ '/' + @page.relative_path_from(@root).sub(@page.extname, '').to_s
11
12
  end
12
13
 
13
14
 
14
15
  def title
15
- 'google'
16
+ @frontmatter['title']
16
17
  end
17
18
 
18
19
 
@@ -0,0 +1,11 @@
1
+ module Flora::Plugins::Blog
2
+
3
+ module PageMethods
4
+
5
+ def posts
6
+ @root.glob('posts/**').map { Post.new(it, @root) }
7
+ end
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,39 @@
1
+ module Flora::Plugins::Redirector
2
+
3
+ module EngineMethods
4
+
5
+ def build(*)
6
+ super
7
+
8
+ @config.redirects.each do |src, dest|
9
+ out_filename = @out_dir.join(src + '.html')
10
+ out_filename.dirname.mkdir unless out_filename.dirname.exist?
11
+ out_filename.write(build_redir_file(dest))
12
+ end
13
+ end
14
+
15
+
16
+ def build_redir_file(url)
17
+ <<~HTML
18
+ <html>
19
+ <head>
20
+ <meta http-equiv="refresh" content="0; url=#{url}" />
21
+ </head>
22
+ </html>
23
+ HTML
24
+ end
25
+
26
+ end
27
+
28
+
29
+ module Config
30
+
31
+ def self.included(base)
32
+ base.class_eval do
33
+ attr_accessor(:redirects)
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,46 @@
1
+ module Flora::Plugins::StaticFiles
2
+
3
+ class Copier
4
+
5
+ def initialize(dir)
6
+ @dir = dir
7
+ end
8
+
9
+
10
+ def copy_to(out)
11
+ @dir.find do |public_file|
12
+ next if public_file.directory?
13
+
14
+ out_filename = out.join(Pathname.new(public_file).relative_path_from(@dir))
15
+ out_filename.dirname.mkdir unless out_filename.dirname.exist?
16
+ out_filename.write(File.read(public_file))
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+
23
+ module EngineMethods
24
+
25
+ def build(*)
26
+ super
27
+
28
+ base = @config.static_files_dir || 'public'
29
+ copier = Copier.new(@path.join(base))
30
+ copier.copy_to(@out_dir.join(base))
31
+ end
32
+
33
+ end
34
+
35
+
36
+ module Config
37
+
38
+ def self.included(base)
39
+ base.class_eval do
40
+ attr_accessor(:static_files_dir)
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ end
data/lib/flora/version.rb CHANGED
@@ -1,3 +1,3 @@
1
- module Flora
2
- VERSION = "0.1.3"
1
+ class Flora
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/flora.rb CHANGED
@@ -5,5 +5,22 @@ require 'kramdown'
5
5
  loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
6
6
  loader.setup
7
7
 
8
- module Flora
8
+ class Flora
9
+
10
+ def initialize(dir)
11
+ # A new class so plugins only affect one instance.
12
+ @engine = Class.new(Engine).new(dir)
13
+ @engine.configure
14
+ end
15
+
16
+
17
+ def build(out)
18
+ @engine.build(out)
19
+ end
20
+
21
+
22
+ def reload_website_code
23
+ @engine.website_loader.reload
24
+ end
25
+
9
26
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flora
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Vladimiroff
@@ -113,11 +113,12 @@ files:
113
113
  - lib/flora/engine/page/html.rb
114
114
  - lib/flora/engine/page/layout.rb
115
115
  - lib/flora/engine/page/markdown_page.rb
116
- - lib/flora/engine/page/plugin_hooks.rb
117
116
  - lib/flora/engine/page/ruby_page.rb
117
+ - lib/flora/plugins/blog.rb
118
+ - lib/flora/plugins/blog/post.rb
119
+ - lib/flora/plugins/redirector.rb
120
+ - lib/flora/plugins/static_files.rb
118
121
  - lib/flora/version.rb
119
- - lib/flora_blog.rb
120
- - lib/flora_blog/post.rb
121
122
  - tmp/.keep
122
123
  homepage: https://code.kat5.dev/nick/flora
123
124
  licenses:
@@ -1,10 +0,0 @@
1
- module Flora::Engine::Page::PluginHooks
2
-
3
- def before_render
4
- end
5
-
6
-
7
- def after_render(html)
8
- end
9
-
10
- end
data/lib/flora_blog.rb DELETED
@@ -1,13 +0,0 @@
1
- module FloraBlog
2
-
3
- module RubyPage
4
-
5
- def render
6
- @posts = @root.glob('posts/**').map { Post.new(it) }
7
-
8
- super
9
- end
10
-
11
- end
12
-
13
- end