From 8b1e5e4349cd189f5bec3fc2a8f3972db6d0890a Mon Sep 17 00:00:00 2001 From: Sami Samhuri Date: Sat, 7 Feb 2026 17:56:14 -0800 Subject: [PATCH] Ruby 4.0.1, update gems, fix lint warnings --- .ruby-version | 2 +- Gemfile | 28 +++---- Gemfile.lock | 114 ++++++++++++++-------------- bake.rb | 123 +++++++++++++++---------------- lib/config/loader.rb | 60 +++++++-------- lib/config/simple_toml.rb | 22 +++--- lib/posts/json_feed.rb | 18 ++--- lib/posts/metadata.rb | 34 ++++----- lib/posts/models.rb | 12 +-- lib/posts/plugin.rb | 12 +-- lib/posts/repo.rb | 32 ++++---- lib/posts/rss_feed.rb | 18 ++--- lib/posts/writer.rb | 44 +++++------ lib/pressa.rb | 16 ++-- lib/projects/models.rb | 6 +- lib/projects/plugin.rb | 20 ++--- lib/site.rb | 2 +- lib/site_generator.rb | 26 +++---- lib/utils/file_writer.rb | 4 +- lib/utils/markdown_renderer.rb | 64 ++++++++-------- lib/views/archive_view.rb | 8 +- lib/views/feed_post_view.rb | 6 +- lib/views/icons.rb | 8 +- lib/views/layout.rb | 131 ++++++++++++++++----------------- lib/views/month_posts_view.rb | 8 +- lib/views/post_view.rb | 12 +-- lib/views/project_view.rb | 43 ++++++----- lib/views/projects_view.rb | 16 ++-- lib/views/recent_posts_view.rb | 6 +- lib/views/year_posts_view.rb | 12 +-- spec/config/loader_spec.rb | 38 +++++----- spec/posts/json_feed_spec.rb | 80 ++++++++++---------- spec/posts/metadata_spec.rb | 24 +++--- spec/posts/repo_spec.rb | 40 +++++----- spec/site_generator_spec.rb | 26 +++---- spec/spec_helper.rb | 2 +- spec/views/layout_spec.rb | 46 ++++++------ 37 files changed, 583 insertions(+), 580 deletions(-) diff --git a/.ruby-version b/.ruby-version index 47b322c..1454f6e 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.4.1 +4.0.1 diff --git a/Gemfile b/Gemfile index 2a0c9c4..d978e31 100644 --- a/Gemfile +++ b/Gemfile @@ -1,19 +1,19 @@ -source 'https://rubygems.org' +source "https://rubygems.org" -ruby '~> 3.4.0' +ruby "~> 4.0.1" -gem 'phlex', '~> 2.3' -gem 'kramdown', '~> 2.5' -gem 'kramdown-parser-gfm', '~> 1.1' -gem 'rouge', '~> 4.6' -gem 'dry-struct', '~> 1.8' -gem 'builder', '~> 3.3' -gem 'bake', '~> 0.20' -gem 'nokogiri', '~> 1.18' +gem "phlex", "~> 2.3" +gem "kramdown", "~> 2.5" +gem "kramdown-parser-gfm", "~> 1.1" +gem "rouge", "~> 4.6" +gem "dry-struct", "~> 1.8" +gem "builder", "~> 3.3" +gem "bake", "~> 0.20" +gem "nokogiri", "~> 1.18" group :development, :test do - gem 'rspec', '~> 3.13' - gem 'guard', '~> 2.18' - gem 'guard-rspec', '~> 4.7' - gem 'standard', '~> 1.43' + gem "rspec", "~> 3.13" + gem "guard", "~> 2.18" + gem "guard-rspec", "~> 4.7" + gem "standard", "~> 1.43" end diff --git a/Gemfile.lock b/Gemfile.lock index 5631ad3..9f0d135 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,20 +5,20 @@ GEM bake (0.24.1) bigdecimal samovar (~> 2.1) - bigdecimal (3.3.1) + bigdecimal (4.0.1) builder (3.3.0) coderay (1.1.3) - concurrent-ruby (1.3.5) + concurrent-ruby (1.3.6) console (1.34.2) fiber-annotation fiber-local (~> 1.1) json diff-lcs (1.6.2) - dry-core (1.1.0) + dry-core (1.2.0) concurrent-ruby (~> 1.0) logger zeitwerk (~> 2.6) - dry-inflector (1.2.0) + dry-inflector (1.3.1) dry-logic (1.6.0) bigdecimal concurrent-ruby (~> 1.0) @@ -29,38 +29,37 @@ GEM dry-types (~> 1.8, >= 1.8.2) ice_nine (~> 0.11) zeitwerk (~> 2.6) - dry-types (1.8.3) - bigdecimal (~> 3.0) + dry-types (1.9.1) + bigdecimal (>= 3.0) concurrent-ruby (~> 1.0) dry-core (~> 1.0) dry-inflector (~> 1.0) dry-logic (~> 1.4) zeitwerk (~> 2.6) - ffi (1.17.2) - ffi (1.17.2-aarch64-linux-gnu) - ffi (1.17.2-aarch64-linux-musl) - ffi (1.17.2-arm-linux-gnu) - ffi (1.17.2-arm-linux-musl) - ffi (1.17.2-arm64-darwin) - ffi (1.17.2-x86-linux-gnu) - ffi (1.17.2-x86-linux-musl) - ffi (1.17.2-x86_64-darwin) - ffi (1.17.2-x86_64-linux-gnu) - ffi (1.17.2-x86_64-linux-musl) + ffi (1.17.3) + ffi (1.17.3-aarch64-linux-gnu) + ffi (1.17.3-aarch64-linux-musl) + ffi (1.17.3-arm-linux-gnu) + ffi (1.17.3-arm-linux-musl) + ffi (1.17.3-arm64-darwin) + ffi (1.17.3-x86-linux-gnu) + ffi (1.17.3-x86-linux-musl) + ffi (1.17.3-x86_64-darwin) + ffi (1.17.3-x86_64-linux-gnu) + ffi (1.17.3-x86_64-linux-musl) fiber-annotation (0.2.0) fiber-local (1.1.0) fiber-storage fiber-storage (1.0.1) formatador (1.2.3) reline - guard (2.19.1) + guard (2.20.1) formatador (>= 0.2.4) listen (>= 2.7, < 4.0) logger (~> 1.6) lumberjack (>= 1.0.12, < 2.0) nenv (~> 0.1) notiffany (~> 0.0) - ostruct (~> 0.6) pry (>= 0.13.0) shellany (~> 0.0) thor (>= 0.18.1) @@ -70,15 +69,16 @@ GEM guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) ice_nine (0.11.2) - io-console (0.8.1) - json (2.16.0) - kramdown (2.5.1) - rexml (>= 3.3.9) + io-console (0.8.2) + json (2.18.1) + kramdown (2.5.2) + rexml (>= 3.4.4) kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) language_server-protocol (3.17.0.5) lint_roller (1.1.0) - listen (3.9.0) + listen (3.10.0) + logger rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) @@ -87,49 +87,53 @@ GEM method_source (1.1.0) mini_portile2 (2.8.9) nenv (0.3.0) - nokogiri (1.18.10) + nokogiri (1.19.0) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.18.10-aarch64-linux-gnu) + nokogiri (1.19.0-aarch64-linux-gnu) racc (~> 1.4) - nokogiri (1.18.10-aarch64-linux-musl) + nokogiri (1.19.0-aarch64-linux-musl) racc (~> 1.4) - nokogiri (1.18.10-arm-linux-gnu) + nokogiri (1.19.0-arm-linux-gnu) racc (~> 1.4) - nokogiri (1.18.10-arm-linux-musl) + nokogiri (1.19.0-arm-linux-musl) racc (~> 1.4) - nokogiri (1.18.10-arm64-darwin) + nokogiri (1.19.0-arm64-darwin) racc (~> 1.4) - nokogiri (1.18.10-x86_64-darwin) + nokogiri (1.19.0-x86_64-darwin) racc (~> 1.4) - nokogiri (1.18.10-x86_64-linux-gnu) + nokogiri (1.19.0-x86_64-linux-gnu) racc (~> 1.4) - nokogiri (1.18.10-x86_64-linux-musl) + nokogiri (1.19.0-x86_64-linux-musl) racc (~> 1.4) notiffany (0.1.3) nenv (~> 0.1) shellany (~> 0.0) - ostruct (0.6.3) parallel (1.27.0) - parser (3.3.10.0) + parser (3.3.10.1) ast (~> 2.4.1) racc - phlex (2.3.1) + phlex (2.4.1) + refract (~> 1.0) zeitwerk (~> 2.7) - prism (1.6.0) - pry (0.15.2) + prism (1.9.0) + pry (0.16.0) coderay (~> 1.1) method_source (~> 1.0) + reline (>= 0.6.0) racc (1.8.1) rainbow (3.1.1) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) + refract (1.1.0) + prism + zeitwerk regexp_parser (2.11.3) reline (0.6.3) io-console (~> 0.5) rexml (3.4.4) - rouge (4.6.1) + rouge (4.7.0) rspec (3.13.2) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) @@ -142,8 +146,8 @@ GEM rspec-mocks (3.13.7) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-support (3.13.6) - rubocop (1.80.2) + rspec-support (3.13.7) + rubocop (1.82.1) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -151,38 +155,38 @@ GEM parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.46.0, < 2.0) + rubocop-ast (>= 1.48.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.48.0) + rubocop-ast (1.49.0) parser (>= 3.3.7.2) - prism (~> 1.4) - rubocop-performance (1.25.0) + prism (~> 1.7) + rubocop-performance (1.26.1) lint_roller (~> 1.1) rubocop (>= 1.75.0, < 2.0) - rubocop-ast (>= 1.38.0, < 2.0) + rubocop-ast (>= 1.47.1, < 2.0) ruby-progressbar (1.13.0) samovar (2.4.1) console (~> 1.0) mapping (~> 1.0) shellany (0.0.1) - standard (1.51.1) + standard (1.53.0) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) - rubocop (~> 1.80.2) + rubocop (~> 1.82.0) standard-custom (~> 1.0.0) standard-performance (~> 1.8) standard-custom (1.0.2) lint_roller (~> 1.0) rubocop (~> 1.50) - standard-performance (1.8.0) + standard-performance (1.9.0) lint_roller (~> 1.1) - rubocop-performance (~> 1.25.0) - thor (1.4.0) + rubocop-performance (~> 1.26.0) + thor (1.5.0) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) - unicode-emoji (4.1.0) - zeitwerk (2.7.3) + unicode-emoji (4.2.0) + zeitwerk (2.7.4) PLATFORMS aarch64-linux-gnu @@ -212,7 +216,7 @@ DEPENDENCIES standard (~> 1.43) RUBY VERSION - ruby 3.4.1p0 + ruby 4.0.1p0 BUNDLED WITH - 2.6.2 + 4.0.3 diff --git a/bake.rb b/bake.rb index 17a1825..0400276 100644 --- a/bake.rb +++ b/bake.rb @@ -1,42 +1,42 @@ # Build tasks for samhuri.net static site generator -require 'etc' -require 'fileutils' +require "etc" +require "fileutils" -DRAFTS_DIR = 'public/drafts'.freeze -PUBLISH_HOST = 'mudge'.freeze -PRODUCTION_PUBLISH_DIR = '/var/www/samhuri.net/public'.freeze -BETA_PUBLISH_DIR = '/var/www/beta.samhuri.net/public'.freeze +DRAFTS_DIR = "public/drafts".freeze +PUBLISH_HOST = "mudge".freeze +PRODUCTION_PUBLISH_DIR = "/var/www/samhuri.net/public".freeze +BETA_PUBLISH_DIR = "/var/www/beta.samhuri.net/public".freeze WATCHABLE_DIRECTORIES = %w[public posts lib].freeze -LINT_TARGETS = %w[bake.rb bin Gemfile lib spec].freeze +LINT_TARGETS = %w[bake.rb Gemfile lib spec].freeze BUILD_TARGETS = %w[debug mudge beta release].freeze # Generate the site in debug mode (localhost:8000) def debug - build('http://localhost:8000') + build("http://localhost:8000") end # Generate the site for the mudge development server def mudge - build('http://mudge:8000') + build("http://mudge:8000") end # Generate the site for beta/staging def beta - build('https://beta.samhuri.net') + build("https://beta.samhuri.net") end # Generate the site for production def release - build('https://samhuri.net') + build("https://samhuri.net") end # Start local development server def serve - require 'webrick' - server = WEBrick::HTTPServer.new(Port: 8000, DocumentRoot: 'www') - trap('INT') { server.shutdown } - puts 'Server running at http://localhost:8000' + require "webrick" + server = WEBrick::HTTPServer.new(Port: 8000, DocumentRoot: "www") + trap("INT") { server.shutdown } + puts "Server running at http://localhost:8000" server.start end @@ -45,11 +45,11 @@ end def new_draft(*title_parts) title, filename = if title_parts.empty? - ['Untitled', next_available_draft] + ["Untitled", next_available_draft] else - given_title = title_parts.join(' ') + given_title = title_parts.join(" ") slug = slugify(given_title) - abort 'Error: title cannot be converted to a filename.' if slug.empty? + abort "Error: title cannot be converted to a filename." if slug.empty? filename = "#{slug}.md" path = draft_path(filename) @@ -64,7 +64,7 @@ def new_draft(*title_parts) File.write(path, content) puts "Created new draft at #{path}" - puts '>>> Contents below <<<' + puts ">>> Contents below <<<" puts puts content end @@ -73,12 +73,12 @@ end # @parameter input_path [String] Draft path or filename in public/drafts. def publish_draft(input_path = nil) if input_path.nil? || input_path.strip.empty? - puts 'Usage: bake publish_draft ' + puts "Usage: bake publish_draft " puts - puts 'Available drafts:' + puts "Available drafts:" drafts = Dir.glob("#{DRAFTS_DIR}/*.md").map { |path| File.basename(path) } if drafts.empty? - puts ' (no drafts found)' + puts " (no drafts found)" else drafts.each { |draft| puts " #{draft}" } end @@ -91,9 +91,9 @@ def publish_draft(input_path = nil) now = Time.now content = File.read(draft_path_value) content.sub!(/^Date:.*$/, "Date: #{ordinal_date(now)}") - content.sub!(/^Timestamp:.*$/, "Timestamp: #{now.strftime('%Y-%m-%dT%H:%M:%S%:z')}") + content.sub!(/^Timestamp:.*$/, "Timestamp: #{now.strftime("%Y-%m-%dT%H:%M:%S%:z")}") - target_dir = "posts/#{now.strftime('%Y/%m')}" + target_dir = "posts/#{now.strftime("%Y/%m")}" FileUtils.mkdir_p(target_dir) target_path = "#{target_dir}/#{draft_file}" @@ -105,13 +105,13 @@ end # Watch content directories and rebuild on every change. # @parameter target [String] One of debug, mudge, beta, or release. -def watch(target: 'debug') - unless command_available?('inotifywait') - abort 'inotifywait is required (install inotify-tools).' +def watch(target: "debug") + unless command_available?("inotifywait") + abort "inotifywait is required (install inotify-tools)." end loop do - abort 'Error: watch failed.' unless system('inotifywait', '-e', 'modify,create,delete,move', *watch_paths) + abort "Error: watch failed." unless system("inotifywait", "-e", "modify,create,delete,move", *watch_paths) puts "changed at #{Time.now}" sleep 2 run_build_target(target) @@ -121,29 +121,29 @@ end # Publish to beta/staging server def publish_beta beta - run_rsync(local_paths: ['www/'], publish_dir: BETA_PUBLISH_DIR, dry_run: false, delete: true) + run_rsync(local_paths: ["www/"], publish_dir: BETA_PUBLISH_DIR, dry_run: false, delete: true) end # Publish to production server def publish release - run_rsync(local_paths: ['www/'], publish_dir: PRODUCTION_PUBLISH_DIR, dry_run: false, delete: true) + run_rsync(local_paths: ["www/"], publish_dir: PRODUCTION_PUBLISH_DIR, dry_run: false, delete: true) end # Clean generated files def clean - FileUtils.rm_rf('www') - puts 'Cleaned www/ directory' + FileUtils.rm_rf("www") + puts "Cleaned www/ directory" end # Run RSpec tests def test - exec 'bundle exec rspec' + exec "bundle exec rspec" end # Run Guard for continuous testing def guard - exec 'bundle exec guard' + exec "bundle exec guard" end # List all available drafts @@ -160,7 +160,7 @@ end # Auto-fix StandardRB issues def lint_fix - exec(*standardrb_command('--fix')) + exec(*standardrb_command("--fix")) end private @@ -168,26 +168,26 @@ private # Build the site with specified URL # @parameter url [String] The site URL to use def build(url) - require_relative 'lib/pressa' + require_relative "lib/pressa" puts "Building site for #{url}..." - site = Pressa.create_site(source_path: '.', url_override: url) + site = Pressa.create_site(source_path: ".", url_override: url) generator = Pressa::SiteGenerator.new(site:) - generator.generate(source_path: '.', target_path: 'www') - puts 'Site built successfully in www/' + generator.generate(source_path: ".", target_path: "www") + puts "Site built successfully in www/" end def run_build_target(target) target_name = target.to_s unless BUILD_TARGETS.include?(target_name) - abort "Error: invalid target '#{target_name}'. Use one of: #{BUILD_TARGETS.join(', ')}" + abort "Error: invalid target '#{target_name}'. Use one of: #{BUILD_TARGETS.join(", ")}" end public_send(target_name) end def watch_paths - WATCHABLE_DIRECTORIES.flat_map { |path| ['-r', path] } + WATCHABLE_DIRECTORIES.flat_map { |path| ["-r", path] } end def standardrb_command(*extra_args) @@ -195,17 +195,17 @@ def standardrb_command(*extra_args) end def run_rsync(local_paths:, publish_dir:, dry_run:, delete:) - command = ['rsync', '-aKv', '-e', 'ssh -4'] - command << '--dry-run' if dry_run - command << '--delete' if delete + command = ["rsync", "-aKv", "-e", "ssh -4"] + command << "--dry-run" if dry_run + command << "--delete" if delete command.concat(local_paths) command << "#{PUBLISH_HOST}:#{publish_dir}" - abort 'Error: rsync failed.' unless system(*command) + abort "Error: rsync failed." unless system(*command) end def resolve_draft_input(input_path) - if input_path.include?('/') - if input_path.start_with?('posts/') + if input_path.include?("/") + if input_path.start_with?("posts/") abort "Error: '#{input_path}' is already published in posts/ directory" end @@ -221,16 +221,15 @@ end def slugify(title) title.downcase - .gsub(/[^a-z0-9\s-]/, '') - .gsub(/\s+/, '-') - .gsub(/-+/, '-') - .gsub(/^-|-$/, '') + .gsub(/[^a-z0-9\s-]/, "") + .gsub(/\s+/, "-").squeeze("-") + .gsub(/^-|-$/, "") end -def next_available_draft(base_filename = 'untitled.md') +def next_available_draft(base_filename = "untitled.md") return base_filename unless File.exist?(draft_path(base_filename)) - name_without_ext = File.basename(base_filename, '.md') + name_without_ext = File.basename(base_filename, ".md") counter = 1 loop do numbered_filename = "#{name_without_ext}-#{counter}.md" @@ -247,7 +246,7 @@ def render_draft_template(title) Author: #{current_author} Title: #{title} Date: unpublished - Timestamp: #{now.strftime('%Y-%m-%dT%H:%M:%S%:z')} + Timestamp: #{now.strftime("%Y-%m-%dT%H:%M:%S%:z")} Tags: --- @@ -258,27 +257,27 @@ def render_draft_template(title) end def current_author - Etc.getlogin || ENV['USER'] || `whoami`.strip -rescue StandardError - ENV['USER'] || `whoami`.strip + Etc.getlogin || ENV["USER"] || `whoami`.strip +rescue + ENV["USER"] || `whoami`.strip end def ordinal_date(time) day = time.day suffix = case day when 1, 21, 31 - 'st' + "st" when 2, 22 - 'nd' + "nd" when 3, 23 - 'rd' + "rd" else - 'th' + "th" end time.strftime("#{day}#{suffix} %B, %Y") end def command_available?(command) - system('which', command, out: File::NULL, err: File::NULL) + system("which", command, out: File::NULL, err: File::NULL) end diff --git a/lib/config/loader.rb b/lib/config/loader.rb index b53e33f..b740676 100644 --- a/lib/config/loader.rb +++ b/lib/config/loader.rb @@ -1,8 +1,8 @@ -require_relative '../site' -require_relative '../posts/plugin' -require_relative '../projects/plugin' -require_relative '../utils/markdown_renderer' -require_relative 'simple_toml' +require_relative "../site" +require_relative "../posts/plugin" +require_relative "../projects/plugin" +require_relative "../utils/markdown_renderer" +require_relative "simple_toml" module Pressa module Config @@ -17,31 +17,31 @@ module Pressa end def build_site(url_override: nil) - site_config = load_toml('site.toml') - projects_config = load_toml('projects.toml') + site_config = load_toml("site.toml") + projects_config = load_toml("projects.toml") - validate_required!(site_config, REQUIRED_SITE_KEYS, context: 'site.toml') + validate_required!(site_config, REQUIRED_SITE_KEYS, context: "site.toml") - site_url = url_override || site_config['url'] - projects_plugin = hash_or_empty(site_config['projects_plugin'], 'site.toml projects_plugin') + site_url = url_override || site_config["url"] + projects_plugin = hash_or_empty(site_config["projects_plugin"], "site.toml projects_plugin") projects = build_projects(projects_config) Site.new( - author: site_config['author'], - email: site_config['email'], - title: site_config['title'], - description: site_config['description'], + author: site_config["author"], + email: site_config["email"], + title: site_config["title"], + description: site_config["description"], url: site_url, - image_url: normalize_image_url(site_config['image_url'], site_url), - scripts: build_scripts(site_config['scripts'], context: 'site.toml scripts'), - styles: build_styles(site_config['styles'], context: 'site.toml styles'), + image_url: normalize_image_url(site_config["image_url"], site_url), + scripts: build_scripts(site_config["scripts"], context: "site.toml scripts"), + styles: build_styles(site_config["styles"], context: "site.toml styles"), plugins: [ Posts::Plugin.new, Projects::Plugin.new( projects:, - scripts: build_scripts(projects_plugin['scripts'], context: 'site.toml projects_plugin.scripts'), - styles: build_styles(projects_plugin['styles'], context: 'site.toml projects_plugin.styles') + scripts: build_scripts(projects_plugin["scripts"], context: "site.toml projects_plugin.scripts"), + styles: build_styles(projects_plugin["styles"], context: "site.toml projects_plugin.styles") ) ], renderers: [ @@ -60,7 +60,7 @@ module Pressa end def build_projects(projects_config) - projects = projects_config['projects'] + projects = projects_config["projects"] raise ValidationError, "Missing required top-level array 'projects' in projects.toml" unless projects raise ValidationError, "Expected 'projects' to be an array in projects.toml" unless projects.is_a?(Array) @@ -72,10 +72,10 @@ module Pressa validate_required!(project, REQUIRED_PROJECT_KEYS, context: "projects.toml project ##{index + 1}") Projects::Project.new( - name: project['name'], - title: project['title'], - description: project['description'], - url: project['url'] + name: project["name"], + title: project["title"], + description: project["description"], + url: project["url"] ) end end @@ -87,7 +87,7 @@ module Pressa return if missing.empty? - raise ValidationError, "Missing required #{context} keys: #{missing.join(', ')}" + raise ValidationError, "Missing required #{context} keys: #{missing.join(", ")}" end def hash_or_empty(value, context) @@ -105,10 +105,10 @@ module Pressa when String Script.new(src: item, defer: true) when Hash - src = item['src'] + src = item["src"] raise ValidationError, "Expected #{context}[#{index}].src to be a String" unless src.is_a?(String) && !src.empty? - defer = item.key?('defer') ? item['defer'] : true + defer = item.key?("defer") ? item["defer"] : true unless [true, false].include?(defer) raise ValidationError, "Expected #{context}[#{index}].defer to be a Boolean" end @@ -128,7 +128,7 @@ module Pressa when String Stylesheet.new(href: item) when Hash - href = item['href'] + href = item["href"] raise ValidationError, "Expected #{context}[#{index}].href to be a String" unless href.is_a?(String) && !href.empty? Stylesheet.new(href:) @@ -147,9 +147,9 @@ module Pressa def normalize_image_url(value, site_url) return nil if value.nil? - return value if value.start_with?('http://', 'https://') + return value if value.start_with?("http://", "https://") - normalized = value.start_with?('/') ? value : "/#{value}" + normalized = value.start_with?("/") ? value : "/#{value}" "#{site_url}#{normalized}" end end diff --git a/lib/config/simple_toml.rb b/lib/config/simple_toml.rb index 948a36f..b182405 100644 --- a/lib/config/simple_toml.rb +++ b/lib/config/simple_toml.rb @@ -1,4 +1,4 @@ -require 'json' +require "json" module Pressa module Config @@ -89,7 +89,7 @@ module Pressa keys.each do |key| cursor[key] ||= {} unless cursor[key].is_a?(Hash) - raise ParseError, "Expected table path '#{keys.join('.')}' at line #{line_number}" + raise ParseError, "Expected table path '#{keys.join(".")}' at line #{line_number}" end cursor = cursor[key] @@ -99,7 +99,7 @@ module Pressa end def parse_path(raw_path, line_number) - keys = raw_path.split('.').map(&:strip) + keys = raw_path.split(".").map(&:strip) if keys.empty? || keys.any? { |part| part.empty? || part !~ /\A[A-Za-z0-9_]+\z/ } raise ParseError, "Invalid table path '#{raw_path}' at line #{line_number}" end @@ -107,7 +107,7 @@ module Pressa end def parse_assignment(source, line_number) - separator = index_of_unquoted(source, '=') + separator = index_of_unquoted(source, "=") raise ParseError, "Invalid assignment at line #{line_number}" unless separator key = source[0...separator].strip @@ -136,7 +136,7 @@ module Pressa if in_string if escaped escaped = false - elsif char == '\\' + elsif char == "\\" escaped = true elsif char == '"' in_string = false @@ -148,9 +148,9 @@ module Pressa case char when '"' in_string = true - when '[', '{' + when "[", "{" depth += 1 - when ']', '}' + when "]", "}" depth -= 1 end end @@ -159,7 +159,7 @@ module Pressa end def strip_comments(line) - output = +'' + output = +"" in_string = false escaped = false @@ -169,7 +169,7 @@ module Pressa if escaped escaped = false - elsif char == '\\' + elsif char == "\\" escaped = true elsif char == '"' in_string = false @@ -182,7 +182,7 @@ module Pressa when '"' in_string = true output << char - when '#' + when "#" break else output << char @@ -200,7 +200,7 @@ module Pressa if in_string if escaped escaped = false - elsif char == '\\' + elsif char == "\\" escaped = true elsif char == '"' in_string = false diff --git a/lib/posts/json_feed.rb b/lib/posts/json_feed.rb index 345ffe7..90507dc 100644 --- a/lib/posts/json_feed.rb +++ b/lib/posts/json_feed.rb @@ -1,6 +1,6 @@ -require 'json' -require_relative '../utils/file_writer' -require_relative '../views/feed_post_view' +require "json" +require_relative "../utils/file_writer" +require_relative "../views/feed_post_view" module Pressa module Posts @@ -18,7 +18,7 @@ module Pressa feed = build_feed(recent) json = JSON.pretty_generate(feed) - file_path = File.join(target_path, 'feed.json') + file_path = File.join(target_path, "feed.json") Utils::FileWriter.write(path: file_path, content: json) end @@ -41,18 +41,18 @@ module Pressa author:, version: FEED_VERSION, authors: [author], - feed_url: @site.url_for('/feed.json'), - language: 'en-CA', + feed_url: @site.url_for("/feed.json"), + language: "en-CA", title: @site.title } end def icon_url - @site.url_for('/images/apple-touch-icon-300.png') + @site.url_for("/images/apple-touch-icon-300.png") end def favicon_url - @site.url_for('/images/apple-touch-icon-80.png') + @site.url_for("/images/apple-touch-icon-80.png") end def feed_item(post) @@ -65,7 +65,7 @@ module Pressa item[:tags] = post.tags unless post.tags.empty? item[:content_html] = content_html item[:title] = post.link_post? ? "→ #{post.title}" : post.title - item[:author] = { name: post.author } + item[:author] = {name: post.author} item[:date_published] = post.date.iso8601 item[:id] = permalink diff --git a/lib/posts/metadata.rb b/lib/posts/metadata.rb index 5c972cd..436fd9c 100644 --- a/lib/posts/metadata.rb +++ b/lib/posts/metadata.rb @@ -1,5 +1,5 @@ -require 'yaml' -require 'date' +require "yaml" +require "date" module Pressa module Posts @@ -28,36 +28,36 @@ module Pressa def validate_required_fields! missing = REQUIRED_FIELDS.reject { |field| @raw.key?(field) } - raise "Missing required fields: #{missing.join(', ')}" unless missing.empty? + raise "Missing required fields: #{missing.join(", ")}" unless missing.empty? end def parse_fields - @title = @raw['Title'] - @author = @raw['Author'] - timestamp = @raw['Timestamp'] + @title = @raw["Title"] + @author = @raw["Author"] + timestamp = @raw["Timestamp"] @date = timestamp.is_a?(String) ? DateTime.parse(timestamp) : timestamp.to_datetime - @formatted_date = @raw['Date'] - @link = @raw['Link'] - @tags = parse_tags(@raw['Tags']) - @scripts = parse_scripts(@raw['Scripts']) - @styles = parse_styles(@raw['Styles']) + @formatted_date = @raw["Date"] + @link = @raw["Link"] + @tags = parse_tags(@raw["Tags"]) + @scripts = parse_scripts(@raw["Scripts"]) + @styles = parse_styles(@raw["Styles"]) end def parse_tags(value) return [] if value.nil? - value.is_a?(Array) ? value : value.split(',').map(&:strip) + value.is_a?(Array) ? value : value.split(",").map(&:strip) end def parse_comma_separated(value) return [] if value.nil? || value.empty? - value.split(',').map(&:strip) + value.split(",").map(&:strip) end def parse_scripts(value) return [] if value.nil? parse_comma_separated(value).map do |src| - Script.new(src: normalize_asset_path(src, 'js'), defer: true) + Script.new(src: normalize_asset_path(src, "js"), defer: true) end end @@ -65,13 +65,13 @@ module Pressa return [] if value.nil? parse_comma_separated(value).map do |href| - Stylesheet.new(href: normalize_asset_path(href, 'css')) + Stylesheet.new(href: normalize_asset_path(href, "css")) end end def normalize_asset_path(path, default_dir) - return path if path.start_with?('http://', 'https://', '/') - return path if path.include?('/') + return path if path.start_with?("http://", "https://", "/") + return path if path.include?("/") "#{default_dir}/#{path}" end diff --git a/lib/posts/models.rb b/lib/posts/models.rb index aecf0cc..05c686c 100644 --- a/lib/posts/models.rb +++ b/lib/posts/models.rb @@ -1,5 +1,5 @@ -require 'dry-struct' -require_relative '../site' +require "dry-struct" +require_relative "../site" module Pressa module Posts @@ -30,11 +30,11 @@ module Pressa end def formatted_month - date.strftime('%B') + date.strftime("%B") end def padded_month - format('%02d', month) + format("%02d", month) end end @@ -45,9 +45,9 @@ module Pressa def self.from_date(date) new( - name: date.strftime('%B'), + name: date.strftime("%B"), number: date.month, - padded: format('%02d', date.month) + padded: format("%02d", date.month) ) end end diff --git a/lib/posts/plugin.rb b/lib/posts/plugin.rb index e5ba904..d6a0dd9 100644 --- a/lib/posts/plugin.rb +++ b/lib/posts/plugin.rb @@ -1,8 +1,8 @@ -require_relative '../plugin' -require_relative 'repo' -require_relative 'writer' -require_relative 'json_feed' -require_relative 'rss_feed' +require_relative "../plugin" +require_relative "repo" +require_relative "writer" +require_relative "json_feed" +require_relative "rss_feed" module Pressa module Posts @@ -10,7 +10,7 @@ module Pressa attr_reader :posts_by_year def setup(site:, source_path:) - posts_dir = File.join(source_path, 'posts') + posts_dir = File.join(source_path, "posts") return unless Dir.exist?(posts_dir) repo = PostRepo.new diff --git a/lib/posts/repo.rb b/lib/posts/repo.rb index 4edce70..47bb26b 100644 --- a/lib/posts/repo.rb +++ b/lib/posts/repo.rb @@ -1,13 +1,13 @@ -require 'kramdown' -require_relative 'models' -require_relative 'metadata' +require "kramdown" +require_relative "models" +require_relative "metadata" module Pressa module Posts class PostRepo EXCERPT_LENGTH = 300 - def initialize(output_path: 'posts') + def initialize(output_path: "posts") @output_path = output_path @posts_by_year = {} end @@ -24,18 +24,18 @@ module Pressa private def enumerate_markdown_files(dir, &block) - Dir.glob(File.join(dir, '**', '*.md')).each(&block) + Dir.glob(File.join(dir, "**", "*.md")).each(&block) end def read_post(file_path) content = File.read(file_path) metadata = PostMetadata.parse(content) - body_markdown = content.sub(/\A---\s*\n.*?\n---\s*\n/m, '') + body_markdown = content.sub(/\A---\s*\n.*?\n---\s*\n/m, "") html_body = render_markdown(body_markdown) - slug = File.basename(file_path, '.md') + slug = File.basename(file_path, ".md") path = generate_path(slug, metadata.date) excerpt = generate_excerpt(body_markdown) @@ -58,9 +58,9 @@ module Pressa def render_markdown(markdown) Kramdown::Document.new( markdown, - input: 'GFM', + input: "GFM", hard_wrap: false, - syntax_highlighter: 'rouge', + syntax_highlighter: "rouge", syntax_highlighter_opts: { line_numbers: false, wrap: true @@ -70,27 +70,27 @@ module Pressa def generate_path(slug, date) year = date.year - month = format('%02d', date.month) + month = format("%02d", date.month) "/#{@output_path}/#{year}/#{month}/#{slug}" end def generate_excerpt(markdown) text = markdown.dup - text.gsub!(/!\[[^\]]*\]\([^)]+\)/, '') - text.gsub!(/!\[[^\]]*\]\[[^\]]+\]/, '') + text.gsub!(/!\[[^\]]*\]\([^)]+\)/, "") + text.gsub!(/!\[[^\]]*\]\[[^\]]+\]/, "") text.gsub!(/\[([^\]]+)\]\([^)]+\)/, '\1') text.gsub!(/\[([^\]]+)\]\[[^\]]+\]/, '\1') - text.gsub!(/(?m)^\[[^\]]+\]:\s*\S.*$/, '') + text.gsub!(/(?m)^\[[^\]]+\]:\s*\S.*$/, "") - text.gsub!(/<[^>]+>/, '') + text.gsub!(/<[^>]+>/, "") - text.gsub!(/\s+/, ' ') + text.gsub!(/\s+/, " ") text.strip! - return '...' if text.empty? + return "..." if text.empty? "#{text[0...EXCERPT_LENGTH]}..." end diff --git a/lib/posts/rss_feed.rb b/lib/posts/rss_feed.rb index 32f3dad..14f3c98 100644 --- a/lib/posts/rss_feed.rb +++ b/lib/posts/rss_feed.rb @@ -1,6 +1,6 @@ -require 'builder' -require_relative '../utils/file_writer' -require_relative '../views/feed_post_view' +require "builder" +require_relative "../utils/file_writer" +require_relative "../views/feed_post_view" module Pressa module Posts @@ -16,15 +16,15 @@ module Pressa xml = Builder::XmlMarkup.new(indent: 2) xml.instruct! :xml, version: "1.0", encoding: "UTF-8" - xml.rss version: "2.0", - "xmlns:atom" => "http://www.w3.org/2005/Atom", - "xmlns:content" => "http://purl.org/rss/1.0/modules/content/" do + xml.rss :version => "2.0", + "xmlns:atom" => "http://www.w3.org/2005/Atom", + "xmlns:content" => "http://purl.org/rss/1.0/modules/content/" do xml.channel do xml.title @site.title xml.link @site.url xml.description @site.description xml.pubDate recent.first.date.rfc822 if recent.any? - xml.tag! "atom:link", href: @site.url_for('/feed.xml'), rel: "self", type: "application/rss+xml" + xml.tag! "atom:link", href: @site.url_for("/feed.xml"), rel: "self", type: "application/rss+xml" recent.each do |post| xml.item do @@ -35,13 +35,13 @@ module Pressa xml.guid permalink, isPermaLink: "true" xml.pubDate post.date.rfc822 xml.author post.author - xml.tag!('content:encoded') { xml.cdata!(render_feed_post(post)) } + xml.tag!("content:encoded") { xml.cdata!(render_feed_post(post)) } end end end end - file_path = File.join(target_path, 'feed.xml') + file_path = File.join(target_path, "feed.xml") Utils::FileWriter.write(path: file_path, content: xml.target!) end diff --git a/lib/posts/writer.rb b/lib/posts/writer.rb index e934a5b..5b8144a 100644 --- a/lib/posts/writer.rb +++ b/lib/posts/writer.rb @@ -1,10 +1,10 @@ -require_relative '../utils/file_writer' -require_relative '../views/layout' -require_relative '../views/post_view' -require_relative '../views/recent_posts_view' -require_relative '../views/archive_view' -require_relative '../views/year_posts_view' -require_relative '../views/month_posts_view' +require_relative "../utils/file_writer" +require_relative "../views/layout" +require_relative "../views/post_view" +require_relative "../views/recent_posts_view" +require_relative "../views/archive_view" +require_relative "../views/year_posts_view" +require_relative "../views/month_posts_view" module Pressa module Posts @@ -28,11 +28,11 @@ module Pressa page_subtitle: nil, canonical_url: @site.url, content: content_view, - page_description: 'Recent posts', - page_type: 'article' + page_description: "Recent posts", + page_type: "article" ) - file_path = File.join(target_path, 'index.html') + file_path = File.join(target_path, "index.html") Utils::FileWriter.write(path: file_path, content: html) end @@ -40,13 +40,13 @@ module Pressa content_view = Views::ArchiveView.new(posts_by_year: @posts_by_year, site: @site) html = render_layout( - page_subtitle: 'Archive', - canonical_url: @site.url_for('/posts/'), + page_subtitle: "Archive", + canonical_url: @site.url_for("/posts/"), content: content_view, - page_description: 'Archive of all posts' + page_description: "Archive of all posts" ) - file_path = File.join(target_path, 'posts', 'index.html') + file_path = File.join(target_path, "posts", "index.html") Utils::FileWriter.write(path: file_path, content: html) end @@ -68,7 +68,7 @@ module Pressa private def write_post(post:, target_path:) - content_view = Views::PostView.new(post:, site: @site, article_class: 'container') + content_view = Views::PostView.new(post:, site: @site, article_class: "container") html = render_layout( page_subtitle: post.title, @@ -77,10 +77,10 @@ module Pressa page_scripts: post.scripts, page_styles: post.styles, page_description: post.excerpt, - page_type: 'article' + page_type: "article" ) - file_path = File.join(target_path, post.path.sub(/^\//, ''), 'index.html') + file_path = File.join(target_path, post.path.sub(/^\//, ""), "index.html") Utils::FileWriter.write(path: file_path, content: html) end @@ -92,10 +92,10 @@ module Pressa canonical_url: @site.url_for("/posts/#{year}/"), content: content_view, page_description: "Archive of all posts from #{year}", - page_type: 'article' + page_type: "article" ) - file_path = File.join(target_path, 'posts', year.to_s, 'index.html') + file_path = File.join(target_path, "posts", year.to_s, "index.html") Utils::FileWriter.write(path: file_path, content: html) end @@ -109,10 +109,10 @@ module Pressa canonical_url: @site.url_for("/posts/#{year}/#{month.padded}/"), content: content_view, page_description: "Archive of all posts from #{title}", - page_type: 'article' + page_type: "article" ) - file_path = File.join(target_path, 'posts', year.to_s, month.padded, 'index.html') + file_path = File.join(target_path, "posts", year.to_s, month.padded, "index.html") Utils::FileWriter.write(path: file_path, content: html) end @@ -123,7 +123,7 @@ module Pressa page_scripts: [], page_styles: [], page_description: nil, - page_type: 'website' + page_type: "website" ) layout = Views::Layout.new( site: @site, diff --git a/lib/pressa.rb b/lib/pressa.rb index bd867d8..56ebd67 100644 --- a/lib/pressa.rb +++ b/lib/pressa.rb @@ -1,13 +1,13 @@ -require_relative 'site' -require_relative 'site_generator' -require_relative 'plugin' -require_relative 'posts/plugin' -require_relative 'projects/plugin' -require_relative 'utils/markdown_renderer' -require_relative 'config/loader' +require_relative "site" +require_relative "site_generator" +require_relative "plugin" +require_relative "posts/plugin" +require_relative "projects/plugin" +require_relative "utils/markdown_renderer" +require_relative "config/loader" module Pressa - def self.create_site(source_path: '.', url_override: nil) + def self.create_site(source_path: ".", url_override: nil) loader = Config::Loader.new(source_path:) loader.build_site(url_override:) end diff --git a/lib/projects/models.rb b/lib/projects/models.rb index 75f7fb1..b013f4c 100644 --- a/lib/projects/models.rb +++ b/lib/projects/models.rb @@ -1,5 +1,5 @@ -require 'dry-struct' -require_relative '../site' +require "dry-struct" +require_relative "../site" module Pressa module Projects @@ -11,7 +11,7 @@ module Pressa def github_path uri = URI.parse(url) - uri.path.sub(/^\//, '') + uri.path.sub(/^\//, "") end def path diff --git a/lib/projects/plugin.rb b/lib/projects/plugin.rb index 2e9c1bd..a7ac289 100644 --- a/lib/projects/plugin.rb +++ b/lib/projects/plugin.rb @@ -1,9 +1,9 @@ -require_relative '../plugin' -require_relative '../utils/file_writer' -require_relative '../views/layout' -require_relative '../views/projects_view' -require_relative '../views/project_view' -require_relative 'models' +require_relative "../plugin" +require_relative "../utils/file_writer" +require_relative "../views/layout" +require_relative "../views/projects_view" +require_relative "../views/project_view" +require_relative "models" module Pressa module Projects @@ -34,12 +34,12 @@ module Pressa html = render_layout( site:, - page_subtitle: 'Projects', - canonical_url: site.url_for('/projects/'), + page_subtitle: "Projects", + canonical_url: site.url_for("/projects/"), content: content_view ) - file_path = File.join(target_path, 'projects', 'index.html') + file_path = File.join(target_path, "projects", "index.html") Utils::FileWriter.write(path: file_path, content: html) end @@ -56,7 +56,7 @@ module Pressa page_description: project.description ) - file_path = File.join(target_path, 'projects', project.name, 'index.html') + file_path = File.join(target_path, "projects", project.name, "index.html") Utils::FileWriter.write(path: file_path, content: html) end diff --git a/lib/site.rb b/lib/site.rb index 03a4726..8020965 100644 --- a/lib/site.rb +++ b/lib/site.rb @@ -1,4 +1,4 @@ -require 'dry-struct' +require "dry-struct" module Pressa module Types diff --git a/lib/site_generator.rb b/lib/site_generator.rb index c45d451..c8b1879 100644 --- a/lib/site_generator.rb +++ b/lib/site_generator.rb @@ -1,5 +1,5 @@ -require 'fileutils' -require_relative 'utils/file_writer' +require "fileutils" +require_relative "utils/file_writer" module Pressa class SiteGenerator @@ -30,7 +30,7 @@ module Pressa target_abs = absolute_path(target_path) return unless contains_path?(container: target_abs, path: source_abs) - raise ArgumentError, 'target_path must not be the same as or contain source_path' + raise ArgumentError, "target_path must not be the same as or contain source_path" end def absolute_path(path) @@ -42,12 +42,12 @@ module Pressa end def copy_static_files(source_path, target_path) - public_dir = File.join(source_path, 'public') + public_dir = File.join(source_path, "public") return unless Dir.exist?(public_dir) - Dir.glob(File.join(public_dir, '**', '*'), File::FNM_DOTMATCH).each do |source_file| + Dir.glob(File.join(public_dir, "**", "*"), File::FNM_DOTMATCH).each do |source_file| next if File.directory?(source_file) - next if File.basename(source_file) == '.' || File.basename(source_file) == '..' + next if File.basename(source_file) == "." || File.basename(source_file) == ".." filename = File.basename(source_file) ext = File.extname(source_file)[1..] @@ -56,7 +56,7 @@ module Pressa next end - relative_path = source_file.sub("#{public_dir}/", '') + relative_path = source_file.sub("#{public_dir}/", "") target_file = File.join(target_path, relative_path) FileUtils.mkdir_p(File.dirname(target_file)) @@ -69,11 +69,11 @@ module Pressa end def process_public_directory(source_path, target_path) - public_dir = File.join(source_path, 'public') + public_dir = File.join(source_path, "public") return unless Dir.exist?(public_dir) site.renderers.each do |renderer| - Dir.glob(File.join(public_dir, '**', '*'), File::FNM_DOTMATCH).each do |source_file| + Dir.glob(File.join(public_dir, "**", "*"), File::FNM_DOTMATCH).each do |source_file| next if File.directory?(source_file) filename = File.basename(source_file) @@ -82,10 +82,10 @@ module Pressa if renderer.can_render_file?(filename:, extension: ext) dir_name = File.dirname(source_file) relative_path = if dir_name == public_dir - '' - else - dir_name.sub("#{public_dir}/", '') - end + "" + else + dir_name.sub("#{public_dir}/", "") + end target_dir = File.join(target_path, relative_path) renderer.render(site:, file_path: source_file, target_dir:) diff --git a/lib/utils/file_writer.rb b/lib/utils/file_writer.rb index 9dafed7..96331ce 100644 --- a/lib/utils/file_writer.rb +++ b/lib/utils/file_writer.rb @@ -1,11 +1,11 @@ -require 'fileutils' +require "fileutils" module Pressa module Utils class FileWriter def self.write(path:, content:, permissions: 0o644) FileUtils.mkdir_p(File.dirname(path)) - File.write(path, content, mode: 'w') + File.write(path, content, mode: "w") File.chmod(permissions, path) end diff --git a/lib/utils/markdown_renderer.rb b/lib/utils/markdown_renderer.rb index 32ca3f1..b5ca515 100644 --- a/lib/utils/markdown_renderer.rb +++ b/lib/utils/markdown_renderer.rb @@ -1,9 +1,9 @@ -require 'kramdown' -require 'yaml' -require_relative 'file_writer' -require_relative '../site' -require_relative '../views/layout' -require_relative '../views/icons' +require "kramdown" +require "yaml" +require_relative "file_writer" +require_relative "../site" +require_relative "../views/layout" +require_relative "../views/icons" module Pressa module Utils @@ -11,7 +11,7 @@ module Pressa EXCERPT_LENGTH = 300 def can_render_file?(filename:, extension:) - extension == 'md' + extension == "md" end def render(site:, file_path:, target_dir:) @@ -20,21 +20,21 @@ module Pressa html_body = render_markdown(body_markdown) - page_title = presence(metadata['Title']) || File.basename(file_path, '.md').capitalize - page_type = presence(metadata['Page type']) || 'website' - page_description = presence(metadata['Description']) || generate_excerpt(body_markdown) - show_extension = ['true', 'yes', true].include?(metadata['Show extension']) + page_title = presence(metadata["Title"]) || File.basename(file_path, ".md").capitalize + page_type = presence(metadata["Page type"]) || "website" + page_description = presence(metadata["Description"]) || generate_excerpt(body_markdown) + show_extension = ["true", "yes", true].include?(metadata["Show extension"]) - slug = File.basename(file_path, '.md') + slug = File.basename(file_path, ".md") - relative_dir = File.dirname(file_path).sub(/^.*?\/public\/?/, '') - relative_dir = '' if relative_dir == '.' + relative_dir = File.dirname(file_path).sub(/^.*?\/public\/?/, "") + relative_dir = "" if relative_dir == "." canonical_path = if show_extension - "/#{relative_dir}/#{slug}.html".squeeze('/') - else - "/#{relative_dir}/#{slug}/".squeeze('/') - end + "/#{relative_dir}/#{slug}.html".squeeze("/") + else + "/#{relative_dir}/#{slug}/".squeeze("/") + end html = render_layout( site:, @@ -46,10 +46,10 @@ module Pressa ) output_filename = if show_extension - "#{slug}.html" - else - File.join(slug, 'index.html') - end + "#{slug}.html" + else + File.join(slug, "index.html") + end output_path = File.join(target_dir, output_filename) FileWriter.write(path: output_path, content: html) @@ -71,9 +71,9 @@ module Pressa def render_markdown(markdown) Kramdown::Document.new( markdown, - input: 'GFM', + input: "GFM", hard_wrap: false, - syntax_highlighter: 'rouge', + syntax_highlighter: "rouge", syntax_highlighter_opts: { line_numbers: false, wrap: true @@ -101,13 +101,13 @@ module Pressa end def view_template - article(class: 'container') do + article(class: "container") do h1 { @page_title } raw(safe(@body)) end - div(class: 'row clearfix') do - p(class: 'fin') do + div(class: "row clearfix") do + p(class: "fin") do raw(safe(Views::Icons.code)) end end @@ -118,18 +118,18 @@ module Pressa text = markdown.dup # Drop inline and reference-style images before links are simplified. - text.gsub!(/!\[[^\]]*\]\([^)]+\)/, '') - text.gsub!(/!\[[^\]]*\]\[[^\]]+\]/, '') + text.gsub!(/!\[[^\]]*\]\([^)]+\)/, "") + text.gsub!(/!\[[^\]]*\]\[[^\]]+\]/, "") # Replace inline and reference links with just their text. text.gsub!(/\[([^\]]+)\]\([^)]+\)/, '\1') text.gsub!(/\[([^\]]+)\]\[[^\]]+\]/, '\1') # Remove link reference definitions such as: [foo]: http://example.com - text.gsub!(/(?m)^\[[^\]]+\]:\s*\S.*$/, '') + text.gsub!(/(?m)^\[[^\]]+\]:\s*\S.*$/, "") - text.gsub!(/<[^>]+>/, '') - text.gsub!(/\s+/, ' ') + text.gsub!(/<[^>]+>/, "") + text.gsub!(/\s+/, " ") text.strip! return nil if text.empty? diff --git a/lib/views/archive_view.rb b/lib/views/archive_view.rb index 6413a8a..b776267 100644 --- a/lib/views/archive_view.rb +++ b/lib/views/archive_view.rb @@ -1,5 +1,5 @@ -require 'phlex' -require_relative 'year_posts_view' +require "phlex" +require_relative "year_posts_view" module Pressa module Views @@ -10,8 +10,8 @@ module Pressa end def view_template - div(class: 'container') do - h1 { 'Archive' } + div(class: "container") do + h1 { "Archive" } end @posts_by_year.sorted_years.each do |year| diff --git a/lib/views/feed_post_view.rb b/lib/views/feed_post_view.rb index 653e414..cfd001a 100644 --- a/lib/views/feed_post_view.rb +++ b/lib/views/feed_post_view.rb @@ -1,4 +1,4 @@ -require 'phlex' +require "phlex" module Pressa module Views @@ -10,10 +10,10 @@ module Pressa def view_template div do - p(class: 'time') { @post.formatted_date } + p(class: "time") { @post.formatted_date } raw(safe(@post.body)) p do - a(class: 'permalink', href: @site.url_for(@post.path)) { '∞' } + a(class: "permalink", href: @site.url_for(@post.path)) { "∞" } end end end diff --git a/lib/views/icons.rb b/lib/views/icons.rb index 48bf9b7..6a3c7dd 100644 --- a/lib/views/icons.rb +++ b/lib/views/icons.rb @@ -4,19 +4,19 @@ module Pressa module_function def mastodon - svg(class_name: 'icon icon-mastodon', view_box: '0 0 448 512', path: IconPath::MASTODON) + svg(class_name: "icon icon-mastodon", view_box: "0 0 448 512", path: IconPath::MASTODON) end def github - svg(class_name: 'icon icon-github', view_box: '0 0 496 512', path: IconPath::GITHUB) + svg(class_name: "icon icon-github", view_box: "0 0 496 512", path: IconPath::GITHUB) end def rss - svg(class_name: 'icon icon-rss', view_box: '0 0 448 512', path: IconPath::RSS) + svg(class_name: "icon icon-rss", view_box: "0 0 448 512", path: IconPath::RSS) end def code - svg(class_name: 'icon icon-code', view_box: '0 0 640 512', path: IconPath::CODE) + svg(class_name: "icon icon-code", view_box: "0 0 640 512", path: IconPath::CODE) end private_class_method def svg(class_name:, view_box:, path:) diff --git a/lib/views/layout.rb b/lib/views/layout.rb index 483359a..c643d1d 100644 --- a/lib/views/layout.rb +++ b/lib/views/layout.rb @@ -1,5 +1,5 @@ -require 'phlex' -require_relative 'icons' +require "phlex" +require_relative "icons" module Pressa module Views @@ -7,20 +7,19 @@ module Pressa START_YEAR = 2006 attr_reader :site, - :page_subtitle, - :page_description, - :page_type, - :canonical_url, - :page_scripts, - :page_styles, - :content + :page_subtitle, + :page_description, + :page_type, + :canonical_url, + :page_scripts, + :page_styles, + :content def initialize( site:, - page_subtitle: nil, - canonical_url:, + canonical_url:, page_subtitle: nil, page_description: nil, - page_type: 'website', + page_type: "website", page_scripts: [], page_styles: [], content: nil @@ -42,54 +41,54 @@ module Pressa def view_template doctype - html(lang: 'en') do - comment { 'meow' } + html(lang: "en") do + comment { "meow" } head do - meta(charset: 'UTF-8') + meta(charset: "UTF-8") title { full_title } - meta(name: 'twitter:title', content: full_title) - meta(property: 'og:title', content: full_title) - meta(name: 'description', content: description) - meta(name: 'twitter:description', content: description) - meta(property: 'og:description', content: description) - meta(property: 'og:site_name', content: site.title) + meta(name: "twitter:title", content: full_title) + meta(property: "og:title", content: full_title) + meta(name: "description", content: description) + meta(name: "twitter:description", content: description) + meta(property: "og:description", content: description) + meta(property: "og:site_name", content: site.title) - link(rel: 'canonical', href: canonical_url) - meta(name: 'twitter:url', content: canonical_url) - meta(property: 'og:url', content: canonical_url) - meta(property: 'og:image', content: og_image_url) if og_image_url - meta(property: 'og:type', content: page_type) - meta(property: 'article:author', content: site.author) - meta(name: 'twitter:card', content: 'summary') + link(rel: "canonical", href: canonical_url) + meta(name: "twitter:url", content: canonical_url) + meta(property: "og:url", content: canonical_url) + meta(property: "og:image", content: og_image_url) if og_image_url + meta(property: "og:type", content: page_type) + meta(property: "article:author", content: site.author) + meta(name: "twitter:card", content: "summary") link( - rel: 'alternate', - href: site.url_for('/feed.xml'), - type: 'application/rss+xml', + rel: "alternate", + href: site.url_for("/feed.xml"), + type: "application/rss+xml", title: site.title ) link( - rel: 'alternate', - href: site.url_for('/feed.json'), - type: 'application/json', + rel: "alternate", + href: site.url_for("/feed.json"), + type: "application/json", title: site.title ) - meta(name: 'fediverse:creator', content: '@sjs@techhub.social') - link(rel: 'author', type: 'text/plain', href: site.url_for('/humans.txt')) - link(rel: 'icon', type: 'image/png', href: site.url_for('/images/favicon-32x32.png')) - link(rel: 'shortcut icon', href: site.url_for('/images/favicon.icon')) - link(rel: 'apple-touch-icon', href: site.url_for('/images/apple-touch-icon.png')) - link(rel: 'mask-icon', color: '#aa0000', href: site.url_for('/images/safari-pinned-tab.svg')) - link(rel: 'manifest', href: site.url_for('/images/manifest.json')) - meta(name: 'msapplication-config', content: site.url_for('/images/browserconfig.xml')) - meta(name: 'theme-color', content: '#121212') - meta(name: 'viewport', content: 'width=device-width, initial-scale=1.0, viewport-fit=cover') - link(rel: 'dns-prefetch', href: 'https://gist.github.com') + meta(name: "fediverse:creator", content: "@sjs@techhub.social") + link(rel: "author", type: "text/plain", href: site.url_for("/humans.txt")) + link(rel: "icon", type: "image/png", href: site.url_for("/images/favicon-32x32.png")) + link(rel: "shortcut icon", href: site.url_for("/images/favicon.icon")) + link(rel: "apple-touch-icon", href: site.url_for("/images/apple-touch-icon.png")) + link(rel: "mask-icon", color: "#aa0000", href: site.url_for("/images/safari-pinned-tab.svg")) + link(rel: "manifest", href: site.url_for("/images/manifest.json")) + meta(name: "msapplication-config", content: site.url_for("/images/browserconfig.xml")) + meta(name: "theme-color", content: "#121212") + meta(name: "viewport", content: "width=device-width, initial-scale=1.0, viewport-fit=cover") + link(rel: "dns-prefetch", href: "https://gist.github.com") all_styles.each do |style| - link(rel: 'stylesheet', type: 'text/css', href: absolute_asset(style.href)) + link(rel: "stylesheet", type: "text/css", href: absolute_asset(style.href)) end end @@ -127,73 +126,73 @@ module Pressa end def render_header - header(class: 'primary') do - div(class: 'title') do + header(class: "primary") do + div(class: "title") do h1 do a(href: site.url) { site.title } end br h4 do - plain 'By ' - a(href: site.url_for('/about')) { site.author } + plain "By " + a(href: site.url_for("/about")) { site.author } end end - nav(class: 'remote') do + nav(class: "remote") do ul do - li(class: 'mastodon') do - a(rel: 'me', 'aria-label': 'Mastodon', href: 'https://techhub.social/@sjs') do + li(class: "mastodon") do + a(rel: "me", "aria-label": "Mastodon", href: "https://techhub.social/@sjs") do raw(safe(Icons.mastodon)) end end - li(class: 'github') do - a('aria-label': 'GitHub', href: 'https://github.com/samsonjs') do + li(class: "github") do + a("aria-label": "GitHub", href: "https://github.com/samsonjs") do raw(safe(Icons.github)) end end - li(class: 'rss') do - a('aria-label': 'RSS', href: site.url_for('/feed.xml')) do + li(class: "rss") do + a("aria-label": "RSS", href: site.url_for("/feed.xml")) do raw(safe(Icons.rss)) end end end end - nav(class: 'local') do + nav(class: "local") do ul do - li { a(href: site.url_for('/about')) { 'About' } } - li { a(href: site.url_for('/posts')) { 'Archive' } } - li { a(href: site.url_for('/projects')) { 'Projects' } } + li { a(href: site.url_for("/about")) { "About" } } + li { a(href: site.url_for("/posts")) { "Archive" } } + li { a(href: site.url_for("/projects")) { "Projects" } } end end - div(class: 'clearfix') + div(class: "clearfix") end end def render_footer footer do plain "© #{START_YEAR} - #{Time.now.year} " - a(href: site.url_for('/about')) { site.author } + a(href: site.url_for("/about")) { site.author } end end def render_scripts all_scripts.each do |scr| - attrs = { src: script_src(scr.src) } + attrs = {src: script_src(scr.src)} attrs[:defer] = true if scr.defer script(**attrs) end end def script_src(src) - return src if src.start_with?('http://', 'https://') + return src if src.start_with?("http://", "https://") absolute_asset(src) end def absolute_asset(path) - normalized = path.start_with?('/') ? path : "/#{path}" + normalized = path.start_with?("/") ? path : "/#{path}" site.url_for(normalized) end end diff --git a/lib/views/month_posts_view.rb b/lib/views/month_posts_view.rb index b07f7a7..5368609 100644 --- a/lib/views/month_posts_view.rb +++ b/lib/views/month_posts_view.rb @@ -1,5 +1,5 @@ -require 'phlex' -require_relative 'post_view' +require "phlex" +require_relative "post_view" module Pressa module Views @@ -11,12 +11,12 @@ module Pressa end def view_template - div(class: 'container') do + div(class: "container") do h1 { "#{@month_posts.month.name} #{@year}" } end @month_posts.sorted_posts.each do |post| - div(class: 'container') do + div(class: "container") do render PostView.new(post:, site: @site) end end diff --git a/lib/views/post_view.rb b/lib/views/post_view.rb index d6cc155..6c0c4c1 100644 --- a/lib/views/post_view.rb +++ b/lib/views/post_view.rb @@ -1,5 +1,5 @@ -require 'phlex' -require_relative 'icons' +require "phlex" +require_relative "icons" module Pressa module Views @@ -21,14 +21,14 @@ module Pressa end end time { @post.formatted_date } - a(href: @post.path, class: 'permalink') { '∞' } + a(href: @post.path, class: "permalink") { "∞" } end raw(safe(@post.body)) end - div(class: 'row clearfix') do - p(class: 'fin') do + div(class: "row clearfix") do + p(class: "fin") do raw(safe(Icons.code)) end end @@ -39,7 +39,7 @@ module Pressa def article_attributes return {} unless @article_class - { class: @article_class } + {class: @article_class} end end end diff --git a/lib/views/project_view.rb b/lib/views/project_view.rb index 458a6a4..cb867a4 100644 --- a/lib/views/project_view.rb +++ b/lib/views/project_view.rb @@ -1,5 +1,5 @@ -require 'phlex' -require_relative 'icons' +require "phlex" +require_relative "icons" module Pressa module Views @@ -10,44 +10,43 @@ module Pressa end def view_template - article(class: 'container project') do - h1(id: 'project', data: { title: @project.title }) { @project.title } + article(class: "container project") do + h1(id: "project", data: {title: @project.title}) { @project.title } h4 { @project.description } - div(class: 'project-stats') do + div(class: "project-stats") do p do - a(href: @project.url) { 'GitHub' } - plain ' • ' - a(id: 'nstar', href: stargazers_url) - plain ' • ' - a(id: 'nfork', href: network_url) + a(href: @project.url) { "GitHub" } + plain " • " + a(id: "nstar", href: stargazers_url) + plain " • " + a(id: "nfork", href: network_url) end p do - plain 'Last updated on ' - span(id: 'updated') + plain "Last updated on " + span(id: "updated") end end - div(class: 'project-info row clearfix') do - div(class: 'column half') do - h3 { 'Contributors' } - div(id: 'contributors') + div(class: "project-info row clearfix") do + div(class: "column half") do + h3 { "Contributors" } + div(id: "contributors") end - div(class: 'column half') do - h3 { 'Languages' } - div(id: 'langs') + div(class: "column half") do + h3 { "Languages" } + div(id: "langs") end end end - div(class: 'row clearfix') do - p(class: 'fin') do + div(class: "row clearfix") do + p(class: "fin") do raw(safe(Icons.code)) end end - end private diff --git a/lib/views/projects_view.rb b/lib/views/projects_view.rb index 3481a73..01b3cbe 100644 --- a/lib/views/projects_view.rb +++ b/lib/views/projects_view.rb @@ -1,5 +1,5 @@ -require 'phlex' -require_relative 'icons' +require "phlex" +require_relative "icons" module Pressa module Views @@ -10,21 +10,21 @@ module Pressa end def view_template - article(class: 'container') do - h1 { 'Projects' } + article(class: "container") do + h1 { "Projects" } @projects.each do |project| - div(class: 'project-listing') do + div(class: "project-listing") do h4 do a(href: @site.url_for(project.path)) { project.title } end - p(class: 'description') { project.description } + p(class: "description") { project.description } end end end - div(class: 'row clearfix') do - p(class: 'fin') do + div(class: "row clearfix") do + p(class: "fin") do raw(safe(Icons.code)) end end diff --git a/lib/views/recent_posts_view.rb b/lib/views/recent_posts_view.rb index 09d6586..a18bc02 100644 --- a/lib/views/recent_posts_view.rb +++ b/lib/views/recent_posts_view.rb @@ -1,5 +1,5 @@ -require 'phlex' -require_relative 'post_view' +require "phlex" +require_relative "post_view" module Pressa module Views @@ -10,7 +10,7 @@ module Pressa end def view_template - div(class: 'container') do + div(class: "container") do @posts.each do |post| render PostView.new(post:, site: @site) end diff --git a/lib/views/year_posts_view.rb b/lib/views/year_posts_view.rb index 1a9a543..bdbd6d2 100644 --- a/lib/views/year_posts_view.rb +++ b/lib/views/year_posts_view.rb @@ -1,4 +1,4 @@ -require 'phlex' +require "phlex" module Pressa module Views @@ -10,8 +10,8 @@ module Pressa end def view_template - div(class: 'container') do - h2(class: 'year') do + div(class: "container") do + h2(class: "year") do a(href: year_path) { @year.to_s } end @@ -30,13 +30,13 @@ module Pressa def render_month(month_posts) month = month_posts.month - h3(class: 'month') do + h3(class: "month") do a(href: @site.url_for("/posts/#{@year}/#{month.padded}/")) do month.name end end - ul(class: 'archive') do + ul(class: "archive") do month_posts.sorted_posts.each do |post| li do a(href: post_link(post)) { post.title } @@ -51,7 +51,7 @@ module Pressa end def short_date(date) - date.strftime('%-d %b') + date.strftime("%-d %b") end end end diff --git a/spec/config/loader_spec.rb b/spec/config/loader_spec.rb index a2523b5..396f055 100644 --- a/spec/config/loader_spec.rb +++ b/spec/config/loader_spec.rb @@ -1,39 +1,39 @@ -require 'spec_helper' -require 'fileutils' -require 'tmpdir' +require "spec_helper" +require "fileutils" +require "tmpdir" RSpec.describe Pressa::Config::Loader do - describe '#build_site' do - it 'builds a site from site.toml and projects.toml' do + describe "#build_site" do + it "builds a site from site.toml and projects.toml" do with_temp_config do |dir| loader = described_class.new(source_path: dir) site = loader.build_site - expect(site.author).to eq('Sami Samhuri') - expect(site.url).to eq('https://samhuri.net') - expect(site.image_url).to eq('https://samhuri.net/images/me.jpg') - expect(site.styles.map(&:href)).to eq(['css/style.css']) + expect(site.author).to eq("Sami Samhuri") + expect(site.url).to eq("https://samhuri.net") + expect(site.image_url).to eq("https://samhuri.net/images/me.jpg") + expect(site.styles.map(&:href)).to eq(["css/style.css"]) projects_plugin = site.plugins.find { |plugin| plugin.is_a?(Pressa::Projects::Plugin) } expect(projects_plugin).not_to be_nil - expect(projects_plugin.scripts.map(&:src)).to eq(['js/projects.js']) + expect(projects_plugin.scripts.map(&:src)).to eq(["js/projects.js"]) end end - it 'applies url_override and rewrites relative image_url with override host' do + it "applies url_override and rewrites relative image_url with override host" do with_temp_config do |dir| loader = described_class.new(source_path: dir) - site = loader.build_site(url_override: 'https://beta.samhuri.net') + site = loader.build_site(url_override: "https://beta.samhuri.net") - expect(site.url).to eq('https://beta.samhuri.net') - expect(site.image_url).to eq('https://beta.samhuri.net/images/me.jpg') + expect(site.url).to eq("https://beta.samhuri.net") + expect(site.image_url).to eq("https://beta.samhuri.net/images/me.jpg") end end - it 'raises a validation error for missing required site keys' do + it "raises a validation error for missing required site keys" do Dir.mktmpdir do |dir| - File.write(File.join(dir, 'site.toml'), "title = \"x\"\n") - File.write(File.join(dir, 'projects.toml'), '') + File.write(File.join(dir, "site.toml"), "title = \"x\"\n") + File.write(File.join(dir, "projects.toml"), "") loader = described_class.new(source_path: dir) expect { loader.build_site }.to raise_error(Pressa::Config::ValidationError, /Missing required site\.toml keys/) @@ -43,7 +43,7 @@ RSpec.describe Pressa::Config::Loader do def with_temp_config Dir.mktmpdir do |dir| - File.write(File.join(dir, 'site.toml'), <<~TOML) + File.write(File.join(dir, "site.toml"), <<~TOML) author = "Sami Samhuri" email = "sami@samhuri.net" title = "samhuri.net" @@ -58,7 +58,7 @@ RSpec.describe Pressa::Config::Loader do styles = [] TOML - File.write(File.join(dir, 'projects.toml'), <<~TOML) + File.write(File.join(dir, "projects.toml"), <<~TOML) [[projects]] name = "demo" title = "demo" diff --git a/spec/posts/json_feed_spec.rb b/spec/posts/json_feed_spec.rb index 4944deb..b5f041e 100644 --- a/spec/posts/json_feed_spec.rb +++ b/spec/posts/json_feed_spec.rb @@ -1,72 +1,72 @@ -require 'spec_helper' -require 'json' -require 'tmpdir' +require "spec_helper" +require "json" +require "tmpdir" RSpec.describe Pressa::Posts::JSONFeedWriter do let(:site) do Pressa::Site.new( - author: 'Sami Samhuri', - email: 'sami@samhuri.net', - title: 'samhuri.net', - description: 'blog', - url: 'https://samhuri.net', - image_url: 'https://samhuri.net/images/me.jpg' + author: "Sami Samhuri", + email: "sami@samhuri.net", + title: "samhuri.net", + description: "blog", + url: "https://samhuri.net", + image_url: "https://samhuri.net/images/me.jpg" ) end - let(:posts_by_year) { double('posts_by_year', recent_posts: [post]) } + let(:posts_by_year) { double("posts_by_year", recent_posts: [post]) } let(:writer) { described_class.new(site:, posts_by_year:) } - context 'for link posts' do + context "for link posts" do let(:post) do Pressa::Posts::Post.new( - slug: 'github-flow-like-a-pro', - title: 'GitHub Flow Like a Pro', - author: 'Sami Samhuri', - date: DateTime.parse('2015-05-28T07:42:27-07:00'), - formatted_date: '28th May, 2015', - link: 'http://haacked.com/archive/2014/07/28/github-flow-aliases/', - body: '

hello

', - excerpt: 'hello...', - path: '/posts/2015/05/github-flow-like-a-pro' + slug: "github-flow-like-a-pro", + title: "GitHub Flow Like a Pro", + author: "Sami Samhuri", + date: DateTime.parse("2015-05-28T07:42:27-07:00"), + formatted_date: "28th May, 2015", + link: "http://haacked.com/archive/2014/07/28/github-flow-aliases/", + body: "

hello

", + excerpt: "hello...", + path: "/posts/2015/05/github-flow-like-a-pro" ) end - it 'uses permalink as url and keeps external_url for destination links' do + it "uses permalink as url and keeps external_url for destination links" do Dir.mktmpdir do |dir| writer.write_feed(target_path: dir, limit: 30) - feed = JSON.parse(File.read(File.join(dir, 'feed.json'))) - item = feed.fetch('items').first + feed = JSON.parse(File.read(File.join(dir, "feed.json"))) + item = feed.fetch("items").first - expect(item.fetch('id')).to eq('https://samhuri.net/posts/2015/05/github-flow-like-a-pro') - expect(item.fetch('url')).to eq('https://samhuri.net/posts/2015/05/github-flow-like-a-pro') - expect(item.fetch('external_url')).to eq('http://haacked.com/archive/2014/07/28/github-flow-aliases/') + expect(item.fetch("id")).to eq("https://samhuri.net/posts/2015/05/github-flow-like-a-pro") + expect(item.fetch("url")).to eq("https://samhuri.net/posts/2015/05/github-flow-like-a-pro") + expect(item.fetch("external_url")).to eq("http://haacked.com/archive/2014/07/28/github-flow-aliases/") end end end - context 'for regular posts' do + context "for regular posts" do let(:post) do Pressa::Posts::Post.new( - slug: 'swift-optional-or', - title: 'Swift Optional OR', - author: 'Sami Samhuri', - date: DateTime.parse('2017-10-01T10:00:00-07:00'), - formatted_date: '1st October, 2017', - body: '

hello

', - excerpt: 'hello...', - path: '/posts/2017/10/swift-optional-or' + slug: "swift-optional-or", + title: "Swift Optional OR", + author: "Sami Samhuri", + date: DateTime.parse("2017-10-01T10:00:00-07:00"), + formatted_date: "1st October, 2017", + body: "

hello

", + excerpt: "hello...", + path: "/posts/2017/10/swift-optional-or" ) end - it 'omits external_url' do + it "omits external_url" do Dir.mktmpdir do |dir| writer.write_feed(target_path: dir, limit: 30) - feed = JSON.parse(File.read(File.join(dir, 'feed.json'))) - item = feed.fetch('items').first + feed = JSON.parse(File.read(File.join(dir, "feed.json"))) + item = feed.fetch("items").first - expect(item.fetch('url')).to eq('https://samhuri.net/posts/2017/10/swift-optional-or') - expect(item).not_to have_key('external_url') + expect(item.fetch("url")).to eq("https://samhuri.net/posts/2017/10/swift-optional-or") + expect(item).not_to have_key("external_url") end end end diff --git a/spec/posts/metadata_spec.rb b/spec/posts/metadata_spec.rb index 2eb4385..831f212 100644 --- a/spec/posts/metadata_spec.rb +++ b/spec/posts/metadata_spec.rb @@ -1,8 +1,8 @@ -require 'spec_helper' +require "spec_helper" RSpec.describe Pressa::Posts::PostMetadata do - describe '.parse' do - it 'parses valid YAML front-matter' do + describe ".parse" do + it "parses valid YAML front-matter" do content = <<~MARKDOWN --- Title: Test Post @@ -20,19 +20,19 @@ RSpec.describe Pressa::Posts::PostMetadata do metadata = described_class.parse(content) - expect(metadata.title).to eq('Test Post') - expect(metadata.author).to eq('Trent Reznor') - expect(metadata.formatted_date).to eq('5th November, 2025') + expect(metadata.title).to eq("Test Post") + expect(metadata.author).to eq("Trent Reznor") + expect(metadata.formatted_date).to eq("5th November, 2025") expect(metadata.date.year).to eq(2025) expect(metadata.date.month).to eq(11) expect(metadata.date.day).to eq(5) - expect(metadata.link).to eq('https://example.net/external') - expect(metadata.tags).to eq(['Ruby', 'Testing']) - expect(metadata.scripts.map(&:src)).to eq(['js/highlight.js']) - expect(metadata.styles.map(&:href)).to eq(['css/code.css']) + expect(metadata.link).to eq("https://example.net/external") + expect(metadata.tags).to eq(["Ruby", "Testing"]) + expect(metadata.scripts.map(&:src)).to eq(["js/highlight.js"]) + expect(metadata.styles.map(&:href)).to eq(["css/code.css"]) end - it 'raises error when required fields are missing' do + it "raises error when required fields are missing" do content = <<~MARKDOWN --- Title: Incomplete Post @@ -46,7 +46,7 @@ RSpec.describe Pressa::Posts::PostMetadata do }.to raise_error(/Missing required fields/) end - it 'handles posts without optional fields' do + it "handles posts without optional fields" do content = <<~MARKDOWN --- Title: Simple Post diff --git a/spec/posts/repo_spec.rb b/spec/posts/repo_spec.rb index f6d43b7..abd9030 100644 --- a/spec/posts/repo_spec.rb +++ b/spec/posts/repo_spec.rb @@ -1,14 +1,14 @@ -require 'spec_helper' -require 'fileutils' -require 'tmpdir' +require "spec_helper" +require "fileutils" +require "tmpdir" RSpec.describe Pressa::Posts::PostRepo do let(:repo) { described_class.new } - describe '#read_posts' do - it 'reads and organizes posts by year and month' do + describe "#read_posts" do + it "reads and organizes posts by year and month" do Dir.mktmpdir do |tmpdir| - posts_dir = File.join(tmpdir, 'posts', '2025', '11') + posts_dir = File.join(tmpdir, "posts", "2025", "11") FileUtils.mkdir_p(posts_dir) post_content = <<~MARKDOWN @@ -22,25 +22,25 @@ RSpec.describe Pressa::Posts::PostRepo do Had an epic day at Whistler. The powder was deep and the lines were short. MARKDOWN - File.write(File.join(posts_dir, 'shredding.md'), post_content) + File.write(File.join(posts_dir, "shredding.md"), post_content) - posts_by_year = repo.read_posts(File.join(tmpdir, 'posts')) + posts_by_year = repo.read_posts(File.join(tmpdir, "posts")) expect(posts_by_year.all_posts.length).to eq(1) post = posts_by_year.all_posts.first - expect(post.title).to eq('Shredding in November') - expect(post.author).to eq('Shaun White') - expect(post.slug).to eq('shredding') + expect(post.title).to eq("Shredding in November") + expect(post.author).to eq("Shaun White") + expect(post.slug).to eq("shredding") expect(post.year).to eq(2025) expect(post.month).to eq(11) - expect(post.path).to eq('/posts/2025/11/shredding') + expect(post.path).to eq("/posts/2025/11/shredding") end end - it 'generates excerpts from post content' do + it "generates excerpts from post content" do Dir.mktmpdir do |tmpdir| - posts_dir = File.join(tmpdir, 'posts', '2025', '11') + posts_dir = File.join(tmpdir, "posts", "2025", "11") FileUtils.mkdir_p(posts_dir) post_content = <<~MARKDOWN @@ -58,15 +58,15 @@ RSpec.describe Pressa::Posts::PostRepo do More content with a [link](https://example.net). MARKDOWN - File.write(File.join(posts_dir, 'test.md'), post_content) + File.write(File.join(posts_dir, "test.md"), post_content) - posts_by_year = repo.read_posts(File.join(tmpdir, 'posts')) + posts_by_year = repo.read_posts(File.join(tmpdir, "posts")) post = posts_by_year.all_posts.first - expect(post.excerpt).to include('test post') - expect(post.excerpt).not_to include('![') - expect(post.excerpt).to include('link') - expect(post.excerpt).not_to include('[link]') + expect(post.excerpt).to include("test post") + expect(post.excerpt).not_to include("![") + expect(post.excerpt).to include("link") + expect(post.excerpt).not_to include("[link]") end end end diff --git a/spec/site_generator_spec.rb b/spec/site_generator_spec.rb index 786e312..5caf0ba 100644 --- a/spec/site_generator_spec.rb +++ b/spec/site_generator_spec.rb @@ -1,25 +1,25 @@ -require 'spec_helper' -require 'fileutils' -require 'tmpdir' +require "spec_helper" +require "fileutils" +require "tmpdir" RSpec.describe Pressa::SiteGenerator do let(:site) do Pressa::Site.new( - author: 'Sami Samhuri', - email: 'sami@samhuri.net', - title: 'samhuri.net', - description: 'blog', - url: 'https://samhuri.net', + author: "Sami Samhuri", + email: "sami@samhuri.net", + title: "samhuri.net", + description: "blog", + url: "https://samhuri.net", plugins: [], renderers: [] ) end - it 'rejects a target path that matches the source path' do + it "rejects a target path that matches the source path" do Dir.mktmpdir do |dir| - FileUtils.mkdir_p(File.join(dir, 'public')) - source_file = File.join(dir, 'public', 'keep.txt') - File.write(source_file, 'safe') + FileUtils.mkdir_p(File.join(dir, "public")) + source_file = File.join(dir, "public", "keep.txt") + File.write(source_file, "safe") generator = described_class.new(site:) @@ -27,7 +27,7 @@ RSpec.describe Pressa::SiteGenerator do generator.generate(source_path: dir, target_path: dir) }.to raise_error(ArgumentError, /must not be the same as or contain source_path/) - expect(File.read(source_file)).to eq('safe') + expect(File.read(source_file)).to eq("safe") end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 87d14b2..ff5a9c9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,4 @@ -require_relative '../lib/pressa' +require_relative "../lib/pressa" RSpec.configure do |config| config.expect_with :rspec do |expectations| diff --git a/spec/views/layout_spec.rb b/spec/views/layout_spec.rb index 3325ab5..d6417b6 100644 --- a/spec/views/layout_spec.rb +++ b/spec/views/layout_spec.rb @@ -1,45 +1,47 @@ -require 'spec_helper' +require "spec_helper" RSpec.describe Pressa::Views::Layout do - class TestContentView < Phlex::HTML - def view_template - article do - h1 { 'Hello' } + let(:test_content_view) do + Class.new(Phlex::HTML) do + def view_template + article do + h1 { "Hello" } + end end - end + end.new end let(:site) do Pressa::Site.new( - author: 'Sami Samhuri', - email: 'sami@samhuri.net', - title: 'samhuri.net', - description: 'blog', - url: 'https://samhuri.net' + author: "Sami Samhuri", + email: "sami@samhuri.net", + title: "samhuri.net", + description: "blog", + url: "https://samhuri.net" ) end - it 'renders child components as HTML instead of escaped text' do + it "renders child components as HTML instead of escaped text" do html = described_class.new( site:, - canonical_url: 'https://samhuri.net/posts/', - content: TestContentView.new + canonical_url: "https://samhuri.net/posts/", + content: test_content_view ).call - expect(html).to include('
') - expect(html).to include('

Hello

') - expect(html).not_to include('<article>') + expect(html).to include("
") + expect(html).to include("

Hello

") + expect(html).not_to include("<article>") end - it 'keeps escaping enabled for untrusted string fields' do - subtitle = '' + it "keeps escaping enabled for untrusted string fields" do + subtitle = "" html = described_class.new( site:, - canonical_url: 'https://samhuri.net/posts/', + canonical_url: "https://samhuri.net/posts/", page_subtitle: subtitle, - content: TestContentView.new + content: test_content_view ).call - expect(html).to include('samhuri.net: <img src=x onerror=alert(1)>') + expect(html).to include("samhuri.net: <img src=x onerror=alert(1)>") end end