Skip to content

Commit

Permalink
Refactor Site into multiple Concerns (#51)
Browse files Browse the repository at this point in the history
* Refactor Site into multiple Concerns

* Couple more method reshuffles
  • Loading branch information
jaredcwhite authored May 15, 2020
1 parent a48d435 commit 2e9110e
Show file tree
Hide file tree
Showing 14 changed files with 501 additions and 527 deletions.
5 changes: 3 additions & 2 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ Lint/UnreachableCode:
Severity: error
Lint/Void:
Exclude:
- bridgetown-core/lib/bridgetown-core/site.rb
- bridgetown-core/lib/bridgetown-core/concerns/site/configurable.rb
Metrics/AbcSize:
Max: 21
Exclude:
- bridgetown-core/lib/bridgetown-core/concerns/site/configurable.rb
- bridgetown-core/lib/bridgetown-core/commands/plugins.rb
Metrics/BlockLength:
Exclude:
Expand All @@ -67,7 +68,6 @@ Metrics/ClassLength:
- !ruby/regexp /bridgetown-core/features\/.*.rb$/
- !ruby/regexp /bridgetown-core/test\/.*.rb$/
- bridgetown-core/lib/bridgetown-core/document.rb
- bridgetown-core/lib/bridgetown-core/site.rb
- bridgetown-core/lib/bridgetown-core/commands/serve.rb
- bridgetown-core/lib/bridgetown-core/configuration.rb
Max: 240
Expand Down Expand Up @@ -125,6 +125,7 @@ Style/AndOr:
Severity: error
Style/ClassAndModuleChildren:
Exclude:
- bridgetown-core/lib/bridgetown-core/concerns/site/*.rb
- bridgetown-core/test/**/*.rb
Style/FrozenStringLiteralComment:
EnforcedStyle: always
Expand Down
15 changes: 0 additions & 15 deletions bridgetown-core/features/data.feature
Original file line number Diff line number Diff line change
Expand Up @@ -116,18 +116,3 @@ Feature: Data
When I run bridgetown build
Then the "output/index.html" file should exist
And I should see "Dairy Products" in "output/index.html"

Scenario: should be backward compatible with site.data in bridgetown.config.yml
Given I have a "bridgetown.config.yml" file with content:
"""
data:
- name: Jack
age: 28
- name: Leon
age: 34
"""
And I have an "index.html" page that contains "{% for member in site.data %}{{member.name}}{% endfor %}"
When I run bridgetown build
Then the "output/index.html" file should exist
And I should see "Jack" in "output/index.html"
And I should see "Leon" in "output/index.html"
2 changes: 1 addition & 1 deletion bridgetown-core/lib/bridgetown-core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ module Bridgetown
autoload :Cleaner, "bridgetown-core/cleaner"
autoload :Collection, "bridgetown-core/collection"
autoload :Configuration, "bridgetown-core/configuration"
autoload :Convertible, "bridgetown-core/convertible"
autoload :Convertible, "bridgetown-core/concerns/convertible"
autoload :Deprecator, "bridgetown-core/deprecator"
autoload :Document, "bridgetown-core/document"
autoload :EntryFilter, "bridgetown-core/entry_filter"
Expand Down
1 change: 1 addition & 0 deletions bridgetown-core/lib/bridgetown-core/cleaner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def cleanup!
def obsolete_files
out = (existing_files - new_files - new_dirs + replaced_files).to_a
Bridgetown::Hooks.trigger :clean, :on_obsolete, out
@new_files = @new_dirs = nil
out
end

Expand Down
153 changes: 153 additions & 0 deletions bridgetown-core/lib/bridgetown-core/concerns/site/configurable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# frozen_string_literal: true

module Bridgetown
module Site::Configurable
# Public: Set the site's configuration. This handles side-effects caused by
# changing values in the configuration.
#
# config - a Bridgetown::Configuration, containing the new configuration.
#
# Returns the new configuration.
def config=(config)
@config = config.clone

# Source and destination may not be changed after the site has been created.
@root_dir = File.expand_path(config["root_dir"]).freeze
@source = File.expand_path(config["source"]).freeze
@dest = File.expand_path(config["destination"]).freeze
@cache_dir = in_root_dir(config["cache_dir"]).freeze

%w(lsi highlighter baseurl exclude include future unpublished
limit_posts keep_files).each do |opt|
send("#{opt}=", config[opt])
end

configure_cache
configure_component_paths
configure_include_paths
configure_file_read_opts

self.permalink_style = config["permalink"].to_sym

@config
end

# Returns the FrontmatterDefaults or creates a new FrontmatterDefaults
# if it doesn't already exist.
#
# Returns The FrontmatterDefaults
def frontmatter_defaults
@frontmatter_defaults ||= FrontmatterDefaults.new(self)
end

# Whether to perform a full rebuild without incremental regeneration
#
# Returns a Boolean: true for a full rebuild, false for normal build
def incremental?(override = {})
override["incremental"] || config["incremental"]
end

# Returns the publisher or creates a new publisher if it doesn't
# already exist.
#
# Returns The Publisher
def publisher
@publisher ||= Publisher.new(self)
end

# Public: Prefix a given path with the root directory.
#
# paths - (optional) path elements to a file or directory within the
# root directory
#
# Returns a path which is prefixed with the root_dir directory.
def in_root_dir(*paths)
paths.reduce(root_dir) do |base, path|
Bridgetown.sanitized_path(base, path)
end
end

# Public: Prefix a given path with the source directory.
#
# paths - (optional) path elements to a file or directory within the
# source directory
#
# Returns a path which is prefixed with the source directory.
def in_source_dir(*paths)
paths.reduce(source) do |base, path|
Bridgetown.sanitized_path(base, path)
end
end

# Public: Prefix a given path with the destination directory.
#
# paths - (optional) path elements to a file or directory within the
# destination directory
#
# Returns a path which is prefixed with the destination directory.
def in_dest_dir(*paths)
paths.reduce(dest) do |base, path|
Bridgetown.sanitized_path(base, path)
end
end

# Public: Prefix a given path with the cache directory.
#
# paths - (optional) path elements to a file or directory within the
# cache directory
#
# Returns a path which is prefixed with the cache directory.
def in_cache_dir(*paths)
paths.reduce(cache_dir) do |base, path|
Bridgetown.sanitized_path(base, path)
end
end

# Public: The full path to the directory that houses all the collections registered
# with the current site.
#
# Returns the source directory or the absolute path to the custom collections_dir
def collections_path
dir_str = config["collections_dir"]
@collections_path ||= dir_str.empty? ? source : in_source_dir(dir_str)
end

private

# Disable Marshaling cache to disk in Safe Mode
def configure_cache
Bridgetown::Cache.cache_dir = in_root_dir(config["cache_dir"], "Bridgetown/Cache")
Bridgetown::Cache.disable_disk_cache! if config["disable_disk_cache"]
end

def configure_component_paths
# Loop through plugins paths first
plugin_components_load_paths = Bridgetown::PluginManager.source_manifests
.map(&:components).compact

local_components_load_paths = config["components_dir"].yield_self do |dir|
dir.is_a?(Array) ? dir : [dir]
end
local_components_load_paths.map! do |dir|
if !!(dir =~ %r!^\.\.?\/!)
# allow ./dir or ../../dir type options
File.expand_path(dir.to_s, root_dir)
else
in_source_dir(dir.to_s)
end
end

@components_load_paths = plugin_components_load_paths + local_components_load_paths
end

def configure_include_paths
@includes_load_paths = Array(in_source_dir(config["includes_dir"].to_s))
end

def configure_file_read_opts
self.file_read_opts = {}
file_read_opts[:encoding] = config["encoding"] if config["encoding"]
self.file_read_opts = Bridgetown::Utils.merged_file_read_opts(self, {})
end
end
end
111 changes: 111 additions & 0 deletions bridgetown-core/lib/bridgetown-core/concerns/site/content.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# frozen_string_literal: true

module Bridgetown
module Site::Content
# Construct a Hash of Posts indexed by the specified Post attribute.
#
# post_attr - The String name of the Post attribute.
#
# Examples
#
# post_attr_hash('categories')
# # => { 'tech' => [<Post A>, <Post B>],
# # 'ruby' => [<Post B>] }
#
# Returns the Hash: { attr => posts } where
# attr - One of the values for the requested attribute.
# posts - The Array of Posts with the given attr value.
def post_attr_hash(post_attr)
# Build a hash map based on the specified post attribute ( post attr =>
# array of posts ) then sort each array in reverse order.
@post_attr_hash[post_attr] ||= begin
hash = Hash.new { |h, key| h[key] = [] }
posts.docs.each do |p|
p.data[post_attr]&.each { |t| hash[t] << p }
end
hash.each_value { |posts| posts.sort!.reverse! }
hash
end
end

def tags
post_attr_hash("tags")
end

def categories
post_attr_hash("categories")
end

def metadata
data["site_metadata"] ||= ActiveSupport::HashWithIndifferentAccess.new
end

# The Hash payload containing site-wide data.
#
# Returns the Hash: { "site" => data } where data is a Hash with keys:
# "time" - The Time as specified in the configuration or the
# current time if none was specified.
# "posts" - The Array of Posts, sorted chronologically by post date
# and then title.
# "pages" - The Array of all Pages.
# "html_pages" - The Array of HTML Pages.
# "categories" - The Hash of category values and Posts.
# See Site#post_attr_hash for type info.
# "tags" - The Hash of tag values and Posts.
# See Site#post_attr_hash for type info.
def site_payload
Drops::UnifiedPayloadDrop.new self
end
alias_method :to_liquid, :site_payload

# The list of collections and their corresponding Bridgetown::Collection instances.
# If config['collections'] is set, a new instance is created
# for each item in the collection, a new hash is returned otherwise.
#
# Returns a Hash containing collection name-to-instance pairs.
def collections
@collections ||= collection_names.each_with_object(
ActiveSupport::HashWithIndifferentAccess.new
) do |name, hsh|
hsh[name] = Bridgetown::Collection.new(self, name)
end
end

# The list of collection names.
#
# Returns an array of collection names from the configuration,
# or an empty array if the `collections` key is not set.
def collection_names
case config["collections"]
when Hash
config["collections"].keys
when Array
config["collections"]
when nil
[]
else
raise ArgumentError, "Your `collections` key must be a hash or an array."
end
end

# Get all the documents
#
# Returns an Array of all Documents
def documents
collections.each_with_object(Set.new) do |(_, collection), set|
set.merge(collection.docs).merge(collection.files)
end.to_a
end

# Get the to be written documents
#
# Returns an Array of Documents which should be written
def docs_to_write
documents.select(&:write?)
end

def posts
collections["posts"] ||= Collection.new(self, "posts")
end
end
end
56 changes: 56 additions & 0 deletions bridgetown-core/lib/bridgetown-core/concerns/site/extensible.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# frozen_string_literal: true

module Bridgetown
module Site::Extensible
# Load necessary libraries, plugins, converters, and generators.
#
# Returns nothing.
def setup
plugin_manager.require_plugin_files
self.converters = instantiate_subclasses(Bridgetown::Converter)
self.generators = instantiate_subclasses(Bridgetown::Generator)
end

# Run each of the Generators.
#
# Returns nothing.
def generate
generators.each do |generator|
start = Time.now
generator.generate(self)

next unless ENV["BRIDGETOWN_LOG_LEVEL"] == "debug"

generator_name = if generator.class.respond_to?(:custom_name)
generator.class.custom_name
else
generator.class.name
end
Bridgetown.logger.debug "Generating:",
"#{generator_name} finished in #{Time.now - start} seconds."
end
end

# Get the implementation class for the given Converter.
# Returns the Converter instance implementing the given Converter.
# klass - The Class of the Converter to fetch.
def find_converter_instance(klass)
@find_converter_instance ||= {}
@find_converter_instance[klass] ||= begin
converters.find { |converter| converter.instance_of?(klass) } || \
raise("No Converters found for #{klass}")
end
end

# klass - class or module containing the subclasses.
# Returns array of instances of subclasses of parameter.
# Create array of instances of the subclasses of the class or module
# passed in as argument.

def instantiate_subclasses(klass)
klass.descendants.sort.map do |c|
c.new(config)
end
end
end
end
Loading

0 comments on commit 2e9110e

Please sign in to comment.