flora 0.11.0 → 1.0.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 +4 -4
- data/.woodpecker.yml +16 -0
- data/lib/flora/config.rb +13 -9
- data/lib/flora/factory.rb +2 -2
- data/lib/flora/plugin_manager.rb +28 -0
- data/lib/flora/plugins/blog/post.rb +1 -1
- data/lib/flora/plugins/blog.rb +93 -2
- data/lib/flora/plugins/redirector.rb +1 -1
- data/lib/flora/plugins/static_files.rb +12 -1
- data/lib/flora/project/blueprint/{nestable.rb → has_layout.rb} +12 -12
- data/lib/flora/project/blueprint/markdown.rb +1 -1
- data/lib/flora/project/blueprint/ruby_html.rb +2 -2
- data/lib/flora/project/blueprint.rb +5 -11
- data/lib/flora/project.rb +5 -1
- data/lib/flora/version.rb +1 -1
- data/lib/flora.rb +9 -21
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0333fa46d5b40eb06534baaba23d9fd099a2f1384814feb560ff4cdc38702863
|
|
4
|
+
data.tar.gz: ae45ab23580875aa64eddaae0c806cbb1c7f4be9f5cba7283aa3923ffb978802
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c4dcbb9a3237d1d3c7156894555e859532fe63d978d4845ec608367a8044cc43ee4c9f6e15eef0c3c07a77a1d1848a0f1486b8360a01956e9b48e1638c1eca78
|
|
7
|
+
data.tar.gz: a5863189034686838ca35636aea54b07247da127c3cd540b945d8fed9c47873ba42b9bc2346e8675232230bcf846e3f630cb79fcf0328a474fb43cfb83582cdd
|
data/.woodpecker.yml
ADDED
data/lib/flora/config.rb
CHANGED
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
# This is the user-visible configuration class. It lives in ROOT/_config.rb.
|
|
2
2
|
class Flora::Config
|
|
3
3
|
|
|
4
|
-
def initialize(file,
|
|
4
|
+
def initialize(file, plugin_manager)
|
|
5
5
|
@file = file
|
|
6
|
-
@
|
|
7
|
-
load if @file.exist?
|
|
6
|
+
@plugin_manager = plugin_manager
|
|
8
7
|
end
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
def use(mod)
|
|
12
|
-
@
|
|
11
|
+
@plugin_manager.load(mod)
|
|
12
|
+
|
|
13
|
+
# TODO: ideally, this would live in PluginManager, but it needs to pass
|
|
14
|
+
# Config.
|
|
15
|
+
mod.loaded(self) if mod.respond_to?(:loaded)
|
|
13
16
|
end
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
def extend_view(mod)
|
|
17
|
-
|
|
20
|
+
@plugin_manager.global_load(mod)
|
|
18
21
|
end
|
|
19
22
|
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
# :nodoc:
|
|
25
|
+
def load
|
|
26
|
+
return unless @file.exist?
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
end
|
|
28
|
+
instance_eval(@file.read)
|
|
29
|
+
end
|
|
26
30
|
|
|
27
31
|
end
|
data/lib/flora/factory.rb
CHANGED
|
@@ -14,10 +14,10 @@ class Flora::Factory
|
|
|
14
14
|
|
|
15
15
|
@project.blueprints.each do |blueprint|
|
|
16
16
|
out_filename = out_dir.join(blueprint.page_name)
|
|
17
|
+
@logger.debug("[Flora] #{blueprint.file} => #{out_filename}")
|
|
18
|
+
|
|
17
19
|
FileUtils.mkdir_p(out_filename.dirname) unless out_filename.dirname.exist?
|
|
18
20
|
out_filename.write(blueprint.render)
|
|
19
|
-
|
|
20
|
-
@logger.debug("[Flora] #{blueprint.file} => #{out_filename}")
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class Flora::PluginManager
|
|
2
|
+
|
|
3
|
+
def initialize(logger)
|
|
4
|
+
@logger = logger
|
|
5
|
+
@pluggables = {}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def create_pluggable_class(klass)
|
|
10
|
+
@pluggables[klass] = Class.new(klass)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def load(mod)
|
|
15
|
+
@pluggables.each do |base_class, anon_class|
|
|
16
|
+
name = "#{base_class.name.split('::').last}Methods"
|
|
17
|
+
anon_class.include(mod.const_get(name)) if mod.const_defined?(name)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@logger.debug("[Flora] Plugin #{mod} loaded")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def global_load(mod)
|
|
25
|
+
Kernel.include(mod)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
data/lib/flora/plugins/blog.rb
CHANGED
|
@@ -13,10 +13,66 @@ module Flora::Plugins::Blog
|
|
|
13
13
|
@post = Post.new(file, project.dir)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Feed
|
|
20
|
+
|
|
21
|
+
def initialize(posts, config)
|
|
22
|
+
@posts = posts
|
|
23
|
+
@config = config
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def render
|
|
28
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
|
29
|
+
xml.feed(xmlns: 'http://www.w3.org/2005/Atom') do
|
|
30
|
+
xml.generator('Flora')
|
|
31
|
+
nokogiri_tag(xml, :title, @config.blog[:title])
|
|
32
|
+
xml.updated(last_updated.iso8601)
|
|
33
|
+
nokogiri_tag(xml, :link, href: @config.blog[:url])
|
|
34
|
+
xml.id(@config.blog[:url])
|
|
35
|
+
xml.author do
|
|
36
|
+
xml.name(@config.blog[:author])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
@posts.each { to_entry(xml, it) }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
builder.to_xml
|
|
44
|
+
end
|
|
45
|
+
|
|
16
46
|
|
|
17
47
|
private
|
|
18
48
|
|
|
19
|
-
def
|
|
49
|
+
def full_url(post)
|
|
50
|
+
@config.blog[:url] + post.url
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def last_updated
|
|
55
|
+
@posts[0].date
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def to_entry(xml, post)
|
|
60
|
+
xml.entry do
|
|
61
|
+
xml.id(full_url(post))
|
|
62
|
+
nokogiri_tag(xml, :title, post.title)
|
|
63
|
+
# TODO: actually support published vs updated.
|
|
64
|
+
xml.updated(post.date.iso8601)
|
|
65
|
+
xml.published(post.date.iso8601)
|
|
66
|
+
nokogiri_tag(xml, :link, rel: 'alternate', href: full_url(post))
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# Nokogiri's builder uses method_missing, and some of Lilac's tags are
|
|
72
|
+
# named the same as some of the Atom tags we want to use which causes
|
|
73
|
+
# conflicts. Use this method to get around that!
|
|
74
|
+
def nokogiri_tag(xml, tag, *args, **opts, &block)
|
|
75
|
+
xml.method_missing(tag, *args, **opts, &block)
|
|
20
76
|
end
|
|
21
77
|
|
|
22
78
|
end
|
|
@@ -24,6 +80,13 @@ module Flora::Plugins::Blog
|
|
|
24
80
|
|
|
25
81
|
module ProjectMethods
|
|
26
82
|
|
|
83
|
+
def self.included(base)
|
|
84
|
+
base.class_eval do
|
|
85
|
+
blueprint_classes.prepend(Flora::Plugins::Blog::BlogPost)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
|
|
27
90
|
def posts
|
|
28
91
|
@dir.glob('posts/**.md').map { Post.new(it, @dir) }.sort_by(&:date).reverse
|
|
29
92
|
end
|
|
@@ -31,7 +94,30 @@ module Flora::Plugins::Blog
|
|
|
31
94
|
end
|
|
32
95
|
|
|
33
96
|
|
|
34
|
-
module
|
|
97
|
+
module FactoryMethods
|
|
98
|
+
|
|
99
|
+
def assemble(out_dir)
|
|
100
|
+
super
|
|
101
|
+
|
|
102
|
+
feed = Feed.new(@project.posts, @config)
|
|
103
|
+
out_dir.join('feed.xml').write(feed.render)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
module ConfigMethods
|
|
110
|
+
|
|
111
|
+
def self.included(base)
|
|
112
|
+
base.class_eval do
|
|
113
|
+
attr_accessor(:blog)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
module ViewExtensions
|
|
35
121
|
|
|
36
122
|
def posts
|
|
37
123
|
@project.posts
|
|
@@ -39,4 +125,9 @@ module Flora::Plugins::Blog
|
|
|
39
125
|
|
|
40
126
|
end
|
|
41
127
|
|
|
128
|
+
|
|
129
|
+
def self.loaded(config)
|
|
130
|
+
config.extend_view(ViewExtensions)
|
|
131
|
+
end
|
|
132
|
+
|
|
42
133
|
end
|
|
@@ -19,7 +19,18 @@ module Flora::Plugins::StaticFiles
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
module
|
|
22
|
+
module ProjectMethods
|
|
23
|
+
|
|
24
|
+
def self.included(base)
|
|
25
|
+
base.class_eval do
|
|
26
|
+
blueprint_classes.prepend(Flora::Plugins::StaticFiles::StaticFile)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
module ConfigMethods
|
|
23
34
|
|
|
24
35
|
def self.included(base)
|
|
25
36
|
base.class_eval do
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# A Blueprint that includes
|
|
1
|
+
# A Blueprint that includes HasLayout is something that can be nested into a
|
|
2
2
|
# Lilac-based layout.
|
|
3
|
-
module Flora::Project::Blueprint::
|
|
3
|
+
module Flora::Project::Blueprint::HasLayout
|
|
4
4
|
|
|
5
5
|
def render
|
|
6
6
|
# The Html DSL uses a global variable internally that needs to be reset every time.
|
|
@@ -19,16 +19,15 @@ module Flora::Project::Blueprint::Nestable
|
|
|
19
19
|
|
|
20
20
|
def find_layouts
|
|
21
21
|
layouts = []
|
|
22
|
+
current = @file.parent
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
maybe_layout =
|
|
24
|
+
loop do
|
|
25
|
+
maybe_layout = current.join('./_layout.rb')
|
|
25
26
|
layouts << maybe_layout if maybe_layout.exist?
|
|
26
|
-
end
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
layouts << maybe_layout if maybe_layout.exist?
|
|
28
|
+
break if current == @project.dir
|
|
29
|
+
current = current.parent
|
|
30
|
+
end
|
|
32
31
|
|
|
33
32
|
layouts
|
|
34
33
|
end
|
|
@@ -40,12 +39,13 @@ module Flora::Project::Blueprint::Nestable
|
|
|
40
39
|
end
|
|
41
40
|
|
|
42
41
|
cont = -> { render_internal(layouts, i-1, &block) }
|
|
43
|
-
|
|
42
|
+
filename = layouts[i-1].to_s
|
|
43
|
+
eval_with_block(layouts[i-1].read, filename, &cont)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def eval_with_block(str, &block)
|
|
48
|
-
eval(str)
|
|
47
|
+
def eval_with_block(str, filename, &block)
|
|
48
|
+
eval(str, binding, filename)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
class Flora::Project::Blueprint::RubyHtml < Flora::Project::Blueprint
|
|
2
2
|
|
|
3
|
-
include Flora::Project::Blueprint::
|
|
3
|
+
include Flora::Project::Blueprint::HasLayout
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def self.recognize(file, config)
|
|
@@ -11,7 +11,7 @@ class Flora::Project::Blueprint::RubyHtml < Flora::Project::Blueprint
|
|
|
11
11
|
private
|
|
12
12
|
|
|
13
13
|
def render_lilac
|
|
14
|
-
instance_eval(@file.read)
|
|
14
|
+
instance_eval(@file.read, @file.to_s)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
end
|
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
# A Blueprint is something can be rendered into a
|
|
2
|
-
# looks very different than the
|
|
1
|
+
# A Blueprint is something can be rendered into a page. Sometimes a Blueprint
|
|
2
|
+
# looks very different than the page it renders into, but other times (like for
|
|
3
3
|
# static files), nothing changes.
|
|
4
4
|
class Flora::Project::Blueprint
|
|
5
5
|
|
|
6
6
|
attr_reader(:file)
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
# :nodoc:
|
|
10
|
-
def self.types
|
|
11
|
-
ObjectSpace.each_object(Class).select { it < self }
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
|
|
15
9
|
# Returns true if this Blueprint is appropriate for the file.
|
|
16
10
|
#
|
|
17
11
|
# TODO: should this be an instance method somewhere instead?
|
|
@@ -28,15 +22,15 @@ class Flora::Project::Blueprint
|
|
|
28
22
|
end
|
|
29
23
|
|
|
30
24
|
|
|
31
|
-
# The name of the
|
|
25
|
+
# The name of the page this Blueprint makes. By default this is just the
|
|
32
26
|
# filename, but .html, but subclasses can override this.
|
|
33
27
|
def page_name
|
|
34
28
|
@file.relative_path_from(@project.dir).sub_ext('.html').to_s
|
|
35
29
|
end
|
|
36
30
|
|
|
37
31
|
|
|
38
|
-
# Render a Blueprint into a
|
|
39
|
-
# of whatever the
|
|
32
|
+
# Render a Blueprint into a page for a Website. This is the String contents
|
|
33
|
+
# of whatever the page in the final Website looks like.
|
|
40
34
|
def render
|
|
41
35
|
# Override in subclasses!
|
|
42
36
|
end
|
data/lib/flora/project.rb
CHANGED
|
@@ -9,6 +9,10 @@ class Flora::Project
|
|
|
9
9
|
|
|
10
10
|
IGNORES = ['.git/*', 'lib/**', '**_*.rb']
|
|
11
11
|
|
|
12
|
+
def self.blueprint_classes
|
|
13
|
+
@blueprint_classes ||= [Blueprint::Markdown, Blueprint::RubyHtml]
|
|
14
|
+
end
|
|
15
|
+
|
|
12
16
|
|
|
13
17
|
def initialize(dir, loader, config)
|
|
14
18
|
@dir = dir
|
|
@@ -38,7 +42,7 @@ class Flora::Project
|
|
|
38
42
|
next if IGNORES.any? { file.fnmatch((@dir / it).to_s) }
|
|
39
43
|
next if file.directory?
|
|
40
44
|
|
|
41
|
-
klass =
|
|
45
|
+
klass = self.class.blueprint_classes.find {
|
|
42
46
|
it.recognize(file.relative_path_from(@dir), @config)
|
|
43
47
|
}
|
|
44
48
|
|
data/lib/flora/version.rb
CHANGED
data/lib/flora.rb
CHANGED
|
@@ -5,6 +5,7 @@ require 'fileutils'
|
|
|
5
5
|
require 'logger'
|
|
6
6
|
|
|
7
7
|
loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
|
|
8
|
+
loader.do_not_eager_load("#{__dir__}/flora/plugins")
|
|
8
9
|
loader.setup
|
|
9
10
|
|
|
10
11
|
class Flora
|
|
@@ -16,19 +17,20 @@ class Flora
|
|
|
16
17
|
dir = Pathname.new(dir)
|
|
17
18
|
|
|
18
19
|
@logger = Logger.new(STDOUT, level: ENV['FLORA_LOG'] || 'info')
|
|
20
|
+
@plugin_manager = PluginManager.new(@logger)
|
|
19
21
|
|
|
20
22
|
# Inject Lilac into the Kernel so it's available everywhere. Just
|
|
21
23
|
# instance_eval isn't enough because it'll be missing in lib/ code.
|
|
22
24
|
#
|
|
23
25
|
# TODO: is there a less disruptive way to do this?
|
|
24
|
-
|
|
26
|
+
@plugin_manager.global_load(Flora::Lilac)
|
|
25
27
|
|
|
26
28
|
# The classes that are pluggable get their own instances to avoid conflicting
|
|
27
29
|
# with other instances of Flora in the same process. This is mostly for the
|
|
28
30
|
# unit tests. Maybe one day we can use Ruby::Box or something here instead.
|
|
29
|
-
@config_class =
|
|
30
|
-
@project_class =
|
|
31
|
-
@factory_class =
|
|
31
|
+
@config_class = @plugin_manager.create_pluggable_class(Config)
|
|
32
|
+
@project_class = @plugin_manager.create_pluggable_class(Project)
|
|
33
|
+
@factory_class = @plugin_manager.create_pluggable_class(Factory)
|
|
32
34
|
|
|
33
35
|
# This has to be loaded before Config so Config can reference lib/ code.
|
|
34
36
|
@project_loader = Zeitwerk::Loader.new
|
|
@@ -38,7 +40,9 @@ class Flora
|
|
|
38
40
|
@project_loader.enable_reloading
|
|
39
41
|
@project_loader.setup
|
|
40
42
|
|
|
41
|
-
@config = @config_class.new(dir.join('_config.rb'),
|
|
43
|
+
@config = @config_class.new(dir.join('_config.rb'), @plugin_manager)
|
|
44
|
+
@config.load
|
|
45
|
+
|
|
42
46
|
@project = @project_class.new(dir, @project_loader, @config)
|
|
43
47
|
@factory = @factory_class.new(@project, @config, @logger)
|
|
44
48
|
end
|
|
@@ -60,17 +64,6 @@ class Flora
|
|
|
60
64
|
end
|
|
61
65
|
|
|
62
66
|
|
|
63
|
-
# TODO: it would be nice if this wasn't exposed here. It's just for Config#plugin.
|
|
64
|
-
def load_plugin(mod)
|
|
65
|
-
@config_class.include(mod::Config) if defined?(mod::Config)
|
|
66
|
-
@project_class.include(mod::ProjectMethods) if defined?(mod::ProjectMethods)
|
|
67
|
-
@factory_class.include(mod::FactoryMethods) if defined?(mod::FactoryMethods)
|
|
68
|
-
|
|
69
|
-
# TODO: make this a little more resilient (like the other classes).
|
|
70
|
-
Flora::Project::Blueprint.include(mod::BlueprintMethods) if defined?(mod::BlueprintMethods)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
|
|
74
67
|
private
|
|
75
68
|
|
|
76
69
|
def bench
|
|
@@ -80,8 +73,3 @@ class Flora
|
|
|
80
73
|
end
|
|
81
74
|
|
|
82
75
|
end
|
|
83
|
-
|
|
84
|
-
# Built-in Blueprints need to be explicitly loaded or else they won't show up
|
|
85
|
-
# in Blueprint.types.
|
|
86
|
-
loader.load_file("#{__dir__}/flora/project/blueprint/ruby_html.rb")
|
|
87
|
-
loader.load_file("#{__dir__}/flora/project/blueprint/markdown.rb")
|
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.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nick Vladimiroff
|
|
@@ -142,6 +142,7 @@ executables:
|
|
|
142
142
|
extensions: []
|
|
143
143
|
extra_rdoc_files: []
|
|
144
144
|
files:
|
|
145
|
+
- ".woodpecker.yml"
|
|
145
146
|
- LICENSE.txt
|
|
146
147
|
- README.md
|
|
147
148
|
- Rakefile
|
|
@@ -152,14 +153,15 @@ files:
|
|
|
152
153
|
- lib/flora/config.rb
|
|
153
154
|
- lib/flora/factory.rb
|
|
154
155
|
- lib/flora/lilac.rb
|
|
156
|
+
- lib/flora/plugin_manager.rb
|
|
155
157
|
- lib/flora/plugins/blog.rb
|
|
156
158
|
- lib/flora/plugins/blog/post.rb
|
|
157
159
|
- lib/flora/plugins/redirector.rb
|
|
158
160
|
- lib/flora/plugins/static_files.rb
|
|
159
161
|
- lib/flora/project.rb
|
|
160
162
|
- lib/flora/project/blueprint.rb
|
|
163
|
+
- lib/flora/project/blueprint/has_layout.rb
|
|
161
164
|
- lib/flora/project/blueprint/markdown.rb
|
|
162
|
-
- lib/flora/project/blueprint/nestable.rb
|
|
163
165
|
- lib/flora/project/blueprint/ruby_html.rb
|
|
164
166
|
- lib/flora/version.rb
|
|
165
167
|
- tmp/.keep
|
|
@@ -176,7 +178,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
176
178
|
requirements:
|
|
177
179
|
- - ">="
|
|
178
180
|
- !ruby/object:Gem::Version
|
|
179
|
-
version: 3.
|
|
181
|
+
version: 3.4.0
|
|
180
182
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
181
183
|
requirements:
|
|
182
184
|
- - ">="
|