-
Notifications
You must be signed in to change notification settings - Fork 3
wip: add live reload to site generator #103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 20 commits
6997c66
b5dc0d8
aff262d
c452cfa
15cdac0
b495acf
38de6b2
f3d3dd6
feae41c
88b11ee
a05b4c1
636a56d
2831c15
01f4a34
7817e46
46dd8d3
04ea2dd
57dd488
5783036
1c764fc
3d30600
74d1ec3
0d9f5fe
c08bcb9
8d5d20b
23c2257
6911fe6
d16750b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,9 +15,9 @@ def show | |
| # TODO: make up my mind on how to handle templates. | ||
| # elsif @entry.template? | ||
| # don't love it but fix later, lol do not deploy this to untrusted user contexts??? | ||
| # render inline: File.read(File.join(current_notebook.import_path, @entry.source)), layout: "application" | ||
| # render inline: @entry.body, layout: "application" | ||
|
|
||
| elsif @entry.note? || @entry.bookmark? | ||
| elsif @entry.note? || @entry.bookmark? || @entry.template? | ||
| @show_thread = params[:thread].present? | ||
| @renderer = EntryRenderer.new(@entry, remove_subject: true) | ||
| @current_date = @entry.occurred_at.strftime("%Y-%m-%d") | ||
|
|
@@ -38,6 +38,25 @@ def self.controller_path | |
|
|
||
| private | ||
| def set_entry | ||
|
|
||
| # TODO: live-reloading | ||
| # see if the file exists and if it does, import it | ||
|
|
||
| # step 1: does it exist as a file? | ||
| # step 2: is it markdown or yaml? | ||
| # step 2.1: actually this is harder to untangle | ||
| # will have to think about how i want to support the "normal" dump o yaml | ||
| # vs the "adhoc" markdown | ||
| # step 3: parse it & add it. | ||
|
|
||
| if Rails.env.development? | ||
| @entry = EntryImporter.new(current_notebook).resolve_and_import!(params[:id]) | ||
|
|
||
| if @entry | ||
| return | ||
| end | ||
| end | ||
|
|
||
| # quick terrible hack for routing document type entries | ||
| if params[:format] | ||
| identifier = "#{params[:id]}.#{params[:format]}" | ||
|
|
@@ -70,7 +89,7 @@ def serve_blob(blob) | |
| end | ||
|
|
||
| response.headers["Content-Type"] = content_type || ActiveStorage::BaseController::DEFAULT_SEND_FILE_TYPE | ||
| response.headers["Content-Disposition"] = disposition || ActiveStorage::BaseController::DEFAULT_SEND_FILE_DISPOSITION | ||
| # response.headers["Content-Disposition"] = disposition || ActiveStorage::BaseController::DEFAULT_SEND_FILE_DISPOSITION | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ???? |
||
| end | ||
| end | ||
| end | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| class EntryImporter | ||
| attr_reader :current_notebook, :root_path | ||
| def initialize(current_notebook) | ||
| @current_notebook = current_notebook | ||
| @root_path = current_notebook.import_path | ||
| end | ||
|
|
||
| def resolve_and_import!(identifier) | ||
|
|
||
| # case identifier | ||
| # when "stylesheets/application" | ||
| # return render_stylesheet | ||
| # end | ||
| # TODO: replace hacky & brittle with more well-defined search | ||
| # i.e. exact lookups vs just fuzzy searching | ||
| # ALSO: need to think about how this would interact with scss templates. | ||
| file_path = build_file_path(identifier) | ||
| glob_path = file_path + "*" | ||
| file_path = Dir[glob_path].select { |f| f =~ /#{file_path}(\.html|\.md|\.markdown)+/ }.reject {|f| File.directory?(f) }.first | ||
| if file_path && looks_like_text?(file_path) | ||
| # TODO: hrm, should the identifier come from the request? this will blow up somehow | ||
| import!(identifier, file_path) | ||
| else | ||
| puts "couldn't find nothin' to import" | ||
| return nil | ||
| end | ||
| end | ||
|
|
||
| def import!(identifier, file_path) | ||
| entry_attributes = entry_attributes_from_markdown(identifier, file_path) | ||
|
|
||
| entry = add_entry!(entry_attributes) | ||
| end | ||
|
|
||
| def looks_like_text?(file_path) | ||
| file_path =~ /\.(md|markdown|html|erb)$/ | ||
| end | ||
|
|
||
| def build_file_path(path_info) | ||
| clean_path_info = Rack::Utils.clean_path_info(path_info) | ||
| ::File.join(@root_path, clean_path_info) | ||
| end | ||
|
|
||
| def entry_attributes_from_markdown(identifier, md_path) | ||
| loader = FrontMatterParser::Loader::Yaml.new(allowlist_classes: [Time, Date, DateTime]) | ||
| md_parser = FrontMatterParser::SyntaxParser::Md.new | ||
| parsed_file = FrontMatterParser::Parser.new(md_parser, loader: loader).call(File.read(md_path)) | ||
|
|
||
| occurred_at = parsed_file["occurred_at"] | ||
| if occurred_at.blank? | ||
| # let's try to guess it from the file | ||
| basename = File.basename(md_path) | ||
| date = basename.match(/([0-9]{4}-*[0-9]{2}-*[0-9]{2}-*)/).to_a[0] | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. like, c'mon, isn't this nuts? this is a cute hack but if i give up on being able to parse random files in a random folder structure, it's obvious i should just define this in the frontmatter 🤔 |
||
|
|
||
| # most of the time this does the right thing but it does have the habit | ||
| # of sometimes throwing an exception | ||
| begin | ||
| occurred_at = DateTime.parse(date.to_s) | ||
| rescue ArgumentError | ||
| end | ||
|
|
||
| # if still nil, let's look at the file itself | ||
| if occurred_at.nil? | ||
| occurred_at = File.ctime(md_path) | ||
| end | ||
| end | ||
|
|
||
| # TODO: DON'T DO THIS GIT DOESN"T SET CTIME!!!!!! | ||
| created_at = parsed_file["created_at"] || File.ctime(md_path) | ||
| updated_at = parsed_file["updated_at"] || File.mtime(md_path) | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this actually has caused all sorts of annoying issues when regenerating the site, cos the updated at will reflect the disk mtime & not the last time the file was actually modified. |
||
|
|
||
| # if we're parsing front-mattered markdown, you don't get to define an | ||
| # identifier separate from the file's relative path, don't want to deal | ||
| # with collisions etc, too confusing, the ad hoc markdown is for ad hoc | ||
| # files, loosely slapped together! | ||
| # | ||
| # then, we lop off `.md`, `.markdown` and `html` from the suffix | ||
| entry_source = md_path # store the unmodified identifier as the "source" | ||
| identifier = identifier.gsub(/\.(md|markdown|html|erb)/, "") | ||
|
|
||
| # TODO: test this behaviour | ||
| if entry_source =~ /\.erb$/ | ||
| entry_kind = "template" | ||
| else | ||
| entry_kind = nil | ||
| end | ||
|
|
||
| entry_attributes = parsed_file.front_matter.merge({ | ||
| "identifier" => identifier, | ||
| "source" => entry_source, | ||
| "occurred_at" => occurred_at, | ||
| "body" => parsed_file.content, | ||
| "kind" => entry_kind, | ||
| "created_at" => created_at, | ||
| "updated_at" => updated_at, | ||
| skip_local_sync: true | ||
| }).slice(*Entry.accepted_attributes) | ||
|
|
||
| # handle metadata! | ||
| metadata_keys = (parsed_file.front_matter.keys - Entry.accepted_attributes) | ||
| if metadata_keys.any? | ||
| # if the user has specified a non Hash value, ie "metadata: foo", then | ||
| # throw a slightly easier to understand error here, instead of later on | ||
| # when we try to instantiate the Entry object & the error gets thrown there. | ||
| if entry_attributes["metadata"] && !entry_attributes["metadata"].is_a?(Hash) | ||
| raise "I expected the 'metadata' key on #{identifier} to be Hash, but something else is going on." | ||
| end | ||
|
|
||
| entry_attributes["metadata"] ||= {} | ||
| metadata_keys.each do |mkey| | ||
| entry_attributes["metadata"][mkey] ||= parsed_file.front_matter[mkey] | ||
| end | ||
| end | ||
|
|
||
| entry_attributes | ||
| end | ||
|
|
||
| def add_entry!(entry_attributes) | ||
| identifier = entry_attributes["identifier"] | ||
|
|
||
| # find or update the entry | ||
| entry = current_notebook.entries.find_by(identifier: identifier) | ||
|
|
||
| if entry | ||
| entry.update!(entry_attributes) | ||
| else | ||
| entry = current_notebook.entries.create(entry_attributes) | ||
| end | ||
|
|
||
| entry | ||
| end | ||
|
|
||
| def render_stylesheet | ||
| # okay lets do the dumbest thing possible | ||
| # reload the file everytime!!!! | ||
|
|
||
| scss_path = build_file_path("stylesheets/application.css.scss") | ||
|
|
||
| rendered_stylesheet = nil | ||
| if File.exist?(scss_path) | ||
| load_path = File.join(current_notebook.import_path, "stylesheets") | ||
|
|
||
| rendered_css = SassC::Engine.new(File.read(scss_path), { | ||
| filename: "application.css.scss", | ||
| syntax: :scss, | ||
| load_paths: [load_path], | ||
| }).render | ||
|
|
||
| # there can only be ONE application.css | ||
| if to_delete = current_notebook.entries.find_by(identifier: "stylesheets/application.css") | ||
| puts "Destroying extraneous stylesheets/application.css, so it can be replaced." | ||
| to_delete.destroy | ||
| end | ||
|
|
||
| rendered_stylesheet = current_notebook.entries.new | ||
| rendered_stylesheet.identifier = "stylesheets/application.css" | ||
| rendered_stylesheet.kind = :document | ||
| rendered_stylesheet.save! | ||
|
|
||
| blob = ActiveStorage::Blob.create_and_upload!(io: StringIO.new(rendered_css), | ||
| metadata: { analyzed: true }, | ||
| filename: "application.css") | ||
| # blob.analyze | ||
| rendered_stylesheet.files.create(blob_id: blob.id, created_at: blob.created_at) | ||
| end | ||
|
|
||
| rendered_stylesheet | ||
| end | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think i did this in order to support not-yet-important files as entries. but why did i do this in the normal entries controller as opposed to the static entries controller?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh maybe i was trying to make this work in the non-static mode eh