flora 0.6.1 → 0.7.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: 2cda53926c0506b06a0b7cca0ebcdb2864bd7a12ccc8975c4cc2dc4ff0338b6c
4
- data.tar.gz: b0e0583176436b230c4bf48bf17f6a8c038f0ce16384e40e90ca53a5b10c8f6c
3
+ metadata.gz: c1025da710c673e347a8e739d8d378b986386040ec9bdbe97e92ea5639d4a792
4
+ data.tar.gz: 5aa256a0a55e2b4d345af8e63e6cb19f3627f723d24706091505f54d58bdafb0
5
5
  SHA512:
6
- metadata.gz: 6dc9df7c9b65062f47a2c38bd2af801d2bf54f074e98e64a1cc4ca424116a2ae1cf72745620d479cca937eef561e5747e93f5cd786996678c3b39698020e6402
7
- data.tar.gz: 710c07c6e3dcbee42606a571c74006ec02c26eb134c090268bdf84016097312da0daa1fab0ef08d3155ef620ebffc2570c0bbee4d0c46322c5b0aa20cf20a84d
6
+ metadata.gz: b261bf5ab0e0d8c5203de5950792d5f272a7a7fb7da9055ea06a543018387534d7964f6d5dfc4550ed0cbcb6e529240176466c66f720d62445adfd09d87fb168
7
+ data.tar.gz: e275dad3c3090f5aa53a5df129ea0fc3f2a3921ea3c4a35bf335764ae34c902cb3a07f4987dd8f8163f7f02654ad94c922d521a8f515c268293fbd2827afcc38
data/README.md CHANGED
@@ -25,3 +25,24 @@ end
25
25
  ```
26
26
 
27
27
  Install everything with `bundle install`, and then run `bundle exec flora serve` and open up http://localhost:3000!
28
+
29
+
30
+ ## The Flora way
31
+
32
+ Flora is an opinionated static site generator, and you should generally follow those opinions when making a site in Flora.
33
+
34
+ ### Static only
35
+
36
+ Flora will only ever generate static websites. If you need dynamic content, look elsewhere! (I'd suggest Rails!)
37
+
38
+ ### A Ruby-first approach
39
+
40
+ Flora prefers Ruby and you should too. Configuration and HTML-generation code is in Ruby. Not everything makes sense as Ruby (you should write content-heavy pages in Markdown, for example), but it's the default option for solving problems in a Flora project.
41
+
42
+ ### File-based routing
43
+
44
+ Flora doesn't have a routes file. Instead, requests are routed by convention based on your file's path. While this is the default, some things--like the Redirector plugin--can subvert this opinion when needed.
45
+
46
+ ### Unopinionated but pluggable
47
+
48
+ Flora is a generic static site generator and doesn't favor any particular use case. Making Flora do specific things better is achieved using plugins. Flora ships with a few plugins for common use cases like blogs and handling static files.
data/lib/flora/config.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # This is the user-visible configuration class. It lives in ROOT/_config.rb.
1
2
  class Flora::Config
2
3
 
3
4
  def initialize(file, flora)
data/lib/flora/factory.rb CHANGED
@@ -1,19 +1,20 @@
1
+ # Factories assemble Projects into Websites.
1
2
  class Flora::Factory
2
3
 
3
- def initialize(blueprint, config)
4
- @blueprint = blueprint
4
+ def initialize(project, config)
5
+ @project = project
5
6
  @config = config
6
7
  end
7
8
 
8
9
 
9
- # Assemble a blueprint and put it into a directory.
10
+ # Assemble the Project into a Website, and put it into out_dir.
10
11
  def assemble(out_dir)
11
12
  out_dir.mkdir unless out_dir.exist?
12
13
 
13
- @blueprint.each_page do |page|
14
- out_filename = out_dir.join(page.route)
14
+ @project.blueprints.each do |blueprint|
15
+ out_filename = out_dir.join(blueprint.page_name)
15
16
  out_filename.dirname.mkdir unless out_filename.dirname.exist?
16
- out_filename.write(page.render)
17
+ out_filename.write(blueprint.render)
17
18
  end
18
19
  end
19
20
 
@@ -1,9 +1,18 @@
1
1
  module Flora::Plugins::Blog
2
2
 
3
- module PageMethods
3
+ module ProjectMethods
4
4
 
5
5
  def posts
6
- @blueprint.dir.glob('posts/**').map { Post.new(it, @blueprint.dir) }
6
+ @dir.glob('posts/**').map { Post.new(it, @dir) }
7
+ end
8
+
9
+ end
10
+
11
+
12
+ module BlueprintMethods
13
+
14
+ def posts
15
+ @project.posts
7
16
  end
8
17
 
9
18
  end
@@ -1,33 +1,19 @@
1
1
  module Flora::Plugins::StaticFiles
2
2
 
3
- class Copier
3
+ class StaticFile < Flora::Project::Blueprint
4
4
 
5
- def initialize(dir)
6
- @dir = dir
5
+ def self.recognize(file, config)
6
+ file.fnmatch?(config.static_files_dir + '/**')
7
7
  end
8
8
 
9
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
10
+ def render
11
+ @file.read
18
12
  end
19
13
 
20
- end
21
-
22
-
23
- module FactoryMethods
24
-
25
- def assemble(out_dir)
26
- super
27
14
 
28
- base = @config.static_files_dir || 'public'
29
- copier = Copier.new(@blueprint.dir.join(base))
30
- copier.copy_to(out_dir.join(base))
15
+ def page_name
16
+ @file.relative_path_from(@project.dir).to_s
31
17
  end
32
18
 
33
19
  end
@@ -1,4 +1,4 @@
1
- module Flora::Blueprint::Page::Html
1
+ module Flora::Project::Blueprint::Html
2
2
 
3
3
  TAGS = %i[
4
4
  a abbr address area article aside audio
@@ -1,4 +1,12 @@
1
- class Flora::Blueprint::Page::MarkdownPage < Flora::Blueprint::Page
1
+ class Flora::Project::Blueprint::Markdown < Flora::Project::Blueprint
2
+
3
+ include Flora::Project::Blueprint::Nestable
4
+
5
+
6
+ def self.recognize(file, config)
7
+ file.extname == '.md'
8
+ end
9
+
2
10
 
3
11
  private
4
12
 
@@ -0,0 +1,46 @@
1
+ # A Blueprint that includes Nestable is something that can best nested into
2
+ # Flora's HTML DSL layouts.
3
+ module Flora::Project::Blueprint::Nestable
4
+
5
+ def render
6
+ # The Html DSL uses a global variable internally that needs to be reset every time.
7
+ $flora_added = []
8
+
9
+ layouts = find_layouts
10
+ tree = render_internal(layouts, layouts.size) do
11
+ render_tree
12
+ end
13
+
14
+ Flora::Project::Blueprint::Html.to_html(tree)
15
+ end
16
+
17
+
18
+ private
19
+
20
+ def find_layouts
21
+ layouts = []
22
+
23
+ @file.ascend do |dir|
24
+ maybe_layout = dir.join('_layout.rb')
25
+ layouts << maybe_layout if maybe_layout.exist?
26
+ end
27
+
28
+ layouts
29
+ end
30
+
31
+
32
+ def render_internal(layouts, i, &block)
33
+ if i <= 0
34
+ return block.call
35
+ end
36
+
37
+ cont = -> { render_internal(layouts, i-1, &block) }
38
+ eval_with_block(layouts[i-1].read, &cont)
39
+ end
40
+
41
+
42
+ def eval_with_block(str, &block)
43
+ eval(str)
44
+ end
45
+
46
+ end
@@ -0,0 +1,17 @@
1
+ class Flora::Project::Blueprint::RubyHtml < Flora::Project::Blueprint
2
+
3
+ include Flora::Project::Blueprint::Nestable
4
+
5
+
6
+ def self.recognize(file, config)
7
+ file.extname == '.rb'
8
+ end
9
+
10
+
11
+ private
12
+
13
+ def render_tree
14
+ instance_eval(@file.read)
15
+ end
16
+
17
+ end
@@ -0,0 +1,44 @@
1
+ # A Blueprint is something can be rendered into a File. Sometimes a Blueprint
2
+ # looks very different than the File it renders into, but other times (like for
3
+ # static files), nothing changes.
4
+ class Flora::Project::Blueprint
5
+
6
+ attr_reader(:file)
7
+
8
+
9
+ # :nodoc:
10
+ def self.types
11
+ ObjectSpace.each_object(Class).select { it < self }
12
+ end
13
+
14
+
15
+ # Returns true if this Blueprint is appropriate for the file.
16
+ #
17
+ # TODO: should this be an instance method somewhere instead?
18
+ def self.recognize(file, config)
19
+ # Override in subclasses!
20
+
21
+ false
22
+ end
23
+
24
+
25
+ def initialize(file, project)
26
+ @file = file
27
+ @project = project
28
+ end
29
+
30
+
31
+ # The name of the Page this Blueprint makes. By default this is just the
32
+ # filename, but .html, but subclasses can override this.
33
+ def page_name
34
+ @file.relative_path_from(@project.dir).sub_ext('.html').to_s
35
+ end
36
+
37
+
38
+ # Render a Blueprint into a Page for a Website. This is the String contents
39
+ # of whatever the Page in the final Website looks like.
40
+ def render
41
+ # Override in subclasses!
42
+ end
43
+
44
+ end
@@ -0,0 +1,61 @@
1
+ # A Project is a template for a website made in Flora.
2
+ #
3
+ # Projects live in a directory and have:
4
+ # 1) Blueprints, files that can be transformed into another file type (like HTML).
5
+ # 2) Supporting Code, Ruby code that lives in lib/ that Blueprints can depend on.
6
+ class Flora::Project
7
+
8
+ attr_reader(:dir, :blueprints)
9
+
10
+ IGNORES = ['.git/*', 'lib/**', '**_*.rb']
11
+
12
+
13
+ def initialize(dir, config)
14
+ @dir = dir
15
+ @config = config
16
+
17
+ @blueprints = find_blueprints
18
+
19
+ @loader = Zeitwerk::Loader.new
20
+ if has_supporting_code?
21
+ @loader.push_dir(@dir.join('lib').to_s)
22
+ end
23
+ @loader.enable_reloading
24
+ @loader.setup
25
+ @loader.eager_load
26
+ end
27
+
28
+
29
+ def reload
30
+ @loader.reload
31
+ @blueprints = find_blueprints
32
+ end
33
+
34
+
35
+ private
36
+
37
+ def find_blueprints
38
+ blueprints = []
39
+
40
+ @dir.find do |file|
41
+ next if IGNORES.any? { file.fnmatch((@dir / it).to_s) }
42
+ next if file.directory?
43
+
44
+ klass = Blueprint.types.find {
45
+ it.recognize(file.relative_path_from(@dir), @config)
46
+ }
47
+
48
+ next unless klass
49
+
50
+ blueprints << klass.new(file, self)
51
+ end
52
+
53
+ blueprints
54
+ end
55
+
56
+
57
+ def has_supporting_code?
58
+ @dir.join('lib').exist?
59
+ end
60
+
61
+ end
data/lib/flora/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Flora
2
- VERSION = '0.6.1'
2
+ VERSION = '0.7.0'
3
3
  end
data/lib/flora.rb CHANGED
@@ -14,20 +14,18 @@ class Flora
14
14
  # instance_eval isn't enough because they'll be missing in lib/ code.
15
15
  #
16
16
  # TODO: is there a less disruptive way to do this?
17
- Kernel.prepend(Flora::Blueprint::Page::Html)
17
+ Kernel.prepend(Flora::Project::Blueprint::Html)
18
18
 
19
19
  # The classes that are pluggable get their own instances to avoid conflicting
20
20
  # with other instances of Flora in the same process. This is mostly for the
21
21
  # unit tests. Maybe one day we can use Ruby::Box or something here instead.
22
22
  @config_class = Class.new(Config)
23
+ @project_class = Class.new(Project)
23
24
  @factory_class = Class.new(Factory)
24
25
 
25
- # Page plugins get mixed in directly to the page instances by Blueprint.
26
- @page_modules = []
27
-
28
26
  @config = @config_class.new(dir.join('_config.rb'), self)
29
- @blueprint = Blueprint.new(dir, @config, @page_modules)
30
- @factory = @factory_class.new(@blueprint, @config)
27
+ @project = @project_class.new(dir, @config)
28
+ @factory = @factory_class.new(@project, @config)
31
29
  end
32
30
 
33
31
 
@@ -36,17 +34,24 @@ class Flora
36
34
  end
37
35
 
38
36
 
39
- def reload_blueprint
40
- @blueprint.reload
37
+ def reload_project
38
+ @project.reload
41
39
  end
42
40
 
43
41
 
44
42
  # TODO: it would be nice if this wasn't exposed here. It's just for Config#plugin.
45
43
  def load_plugin(mod)
46
- @factory_class.include(mod::FactoryMethods) if defined?(mod::FactoryMethods)
47
44
  @config_class.include(mod::Config) if defined?(mod::Config)
45
+ @project_class.include(mod::ProjectMethods) if defined?(mod::ProjectMethods)
46
+ @factory_class.include(mod::FactoryMethods) if defined?(mod::FactoryMethods)
48
47
 
49
- @page_modules << mod::PageMethods if defined?(mod::PageMethods)
48
+ # TODO: make this a little more resilient (like the other classes).
49
+ Flora::Project::Blueprint.include(mod::BlueprintMethods) if defined?(mod::BlueprintMethods)
50
50
  end
51
51
 
52
52
  end
53
+
54
+ # Built-in Blueprints need to be explicitly loaded or else they won't show up
55
+ # in Blueprint.types.
56
+ loader.load_file("#{__dir__}/flora/project/blueprint/ruby_html.rb")
57
+ 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.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Vladimiroff
@@ -106,12 +106,6 @@ files:
106
106
  - exe/flora
107
107
  - lib/flora.rb
108
108
  - lib/flora/app.rb
109
- - lib/flora/blueprint.rb
110
- - lib/flora/blueprint/page.rb
111
- - lib/flora/blueprint/page/html.rb
112
- - lib/flora/blueprint/page/layout.rb
113
- - lib/flora/blueprint/page/markdown_page.rb
114
- - lib/flora/blueprint/page/ruby_page.rb
115
109
  - lib/flora/cli.rb
116
110
  - lib/flora/config.rb
117
111
  - lib/flora/factory.rb
@@ -119,6 +113,12 @@ files:
119
113
  - lib/flora/plugins/blog/post.rb
120
114
  - lib/flora/plugins/redirector.rb
121
115
  - lib/flora/plugins/static_files.rb
116
+ - lib/flora/project.rb
117
+ - lib/flora/project/blueprint.rb
118
+ - lib/flora/project/blueprint/html.rb
119
+ - lib/flora/project/blueprint/markdown.rb
120
+ - lib/flora/project/blueprint/nestable.rb
121
+ - lib/flora/project/blueprint/ruby_html.rb
122
122
  - lib/flora/version.rb
123
123
  - tmp/.keep
124
124
  homepage: https://code.kat5.dev/nick/flora
@@ -141,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
141
  - !ruby/object:Gem::Version
142
142
  version: '0'
143
143
  requirements: []
144
- rubygems_version: 4.0.0.dev
144
+ rubygems_version: 4.0.3
145
145
  specification_version: 4
146
146
  summary: A static site generator.
147
147
  test_files: []
@@ -1,29 +0,0 @@
1
- class Flora::Blueprint::Page::Layout
2
-
3
- def initialize(layouts)
4
- @layouts = layouts
5
- end
6
-
7
-
8
- def render(&block)
9
- render_internal(@layouts.size - 1, &block)
10
- end
11
-
12
-
13
- private
14
-
15
- def render_internal(i, &block)
16
- if i <= 0
17
- return block.call
18
- end
19
-
20
- cont = -> { render_internal(i-1, &block) }
21
- eval_with_block(@layouts[i].read, &cont)
22
- end
23
-
24
-
25
- def eval_with_block(str, &block)
26
- eval(str)
27
- end
28
-
29
- end
@@ -1,9 +0,0 @@
1
- class Flora::Blueprint::Page::RubyPage < Flora::Blueprint::Page
2
-
3
- private
4
-
5
- def render_tree
6
- instance_eval(@file.read)
7
- end
8
-
9
- end
@@ -1,52 +0,0 @@
1
- # A page is something can be turned into HTML.
2
- class Flora::Blueprint::Page
3
-
4
- attr_reader(:blueprint, :file, :route)
5
-
6
-
7
- def self.for(file)
8
- # TODO: make this a little more extensible so plugins can implement new page
9
- # types.
10
- case file.extname
11
- when '.rb'
12
- RubyPage
13
- when '.md'
14
- MarkdownPage
15
- end
16
- end
17
-
18
-
19
- def initialize(blueprint, file, route)
20
- @blueprint = blueprint
21
- @file = file
22
- @route = route
23
- @layout = find_layouts
24
- end
25
-
26
-
27
- def render
28
- # The Html DSL uses a global variable internally that needs to be reset every time.
29
- $flora_added = []
30
-
31
- tree = @layout.render do
32
- render_tree
33
- end
34
-
35
- Html.to_html(tree)
36
- end
37
-
38
-
39
- private
40
-
41
- def find_layouts
42
- layouts = []
43
-
44
- @file.ascend do |dir|
45
- maybe_layout = dir.join('_layout.rb')
46
- layouts << maybe_layout if maybe_layout.exist?
47
- end
48
-
49
- Layout.new(layouts)
50
- end
51
-
52
- end
@@ -1,64 +0,0 @@
1
- class Flora::Blueprint
2
-
3
- attr_reader(:dir)
4
-
5
- IGNORES = ['.git/*', 'lib/*', '**_layout.rb', '_config.rb']
6
-
7
-
8
- def initialize(dir, config, page_modules)
9
- @dir = dir
10
- @config = config
11
- @page_modules = page_modules
12
-
13
- @pages = load_pages
14
-
15
- @loader = Zeitwerk::Loader.new
16
- if has_supporting_code?
17
- @loader.push_dir(@dir.join('lib').to_s)
18
- end
19
- @loader.enable_reloading
20
- @loader.setup
21
- end
22
-
23
-
24
- def each_page(&block)
25
- @pages.each(&block)
26
- end
27
-
28
-
29
- def reload
30
- @loader.reload
31
- @pages = load_pages
32
- end
33
-
34
-
35
- private
36
-
37
- def load_pages
38
- pages = []
39
-
40
- @dir.find do |file|
41
- next if IGNORES.any? { file.fnmatch((@dir / it).to_s) }
42
- next if file.directory?
43
- next unless supported_file_type?(file)
44
-
45
- route = file.relative_path_from(@dir).sub_ext('.html').to_s
46
- page = Page.for(file).new(self, file, route)
47
- @page_modules.each { |mod| page.extend(mod) }
48
- pages << page
49
- end
50
-
51
- pages
52
- end
53
-
54
-
55
- def supported_file_type?(file)
56
- file.extname == '.rb' || file.extname == '.md'
57
- end
58
-
59
-
60
- def has_supporting_code?
61
- @dir.join('lib').exist?
62
- end
63
-
64
- end