├── .editorconfig ├── .github └── workflows │ ├── coverage.yaml │ ├── documentation.yaml │ └── test.yaml ├── .gitignore ├── .mailmap ├── bin └── rackula ├── config └── sus.rb ├── gems.rb ├── guides ├── getting-started │ └── readme.md └── links.yaml ├── lib ├── rackula.rb └── rackula │ ├── command.rb │ ├── command │ └── generate.rb │ └── version.rb ├── license.md ├── rackula.gemspec ├── readme.md ├── release.cert └── test ├── .site ├── another.html ├── config.ru ├── index.html └── public │ └── document.txt └── rakula.rb /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 2 6 | 7 | [*.{yml,yaml}] 8 | indent_style = space 9 | indent_size = 2 10 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yaml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | env: 9 | CONSOLE_OUTPUT: XTerm 10 | COVERAGE: PartialSummary 11 | 12 | jobs: 13 | test: 14 | name: ${{matrix.ruby}} on ${{matrix.os}} 15 | runs-on: ${{matrix.os}}-latest 16 | 17 | strategy: 18 | matrix: 19 | os: 20 | - ubuntu 21 | - macos 22 | 23 | ruby: 24 | - "3.3" 25 | 26 | steps: 27 | - uses: actions/checkout@v4 28 | - uses: ruby/setup-ruby@v1 29 | with: 30 | ruby-version: ${{matrix.ruby}} 31 | bundler-cache: true 32 | 33 | - name: Run tests 34 | timeout-minutes: 5 35 | run: bundle exec bake test 36 | 37 | - uses: actions/upload-artifact@v3 38 | with: 39 | name: coverage-${{matrix.os}}-${{matrix.ruby}} 40 | path: .covered.db 41 | 42 | validate: 43 | needs: test 44 | runs-on: ubuntu-latest 45 | 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: ruby/setup-ruby@v1 49 | with: 50 | ruby-version: "3.3" 51 | bundler-cache: true 52 | 53 | - uses: actions/download-artifact@v3 54 | 55 | - name: Validate coverage 56 | timeout-minutes: 5 57 | run: bundle exec bake covered:validate --paths */.covered.db \; 58 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yaml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages: 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | # Allow one concurrent deployment: 15 | concurrency: 16 | group: "pages" 17 | cancel-in-progress: true 18 | 19 | env: 20 | CONSOLE_OUTPUT: XTerm 21 | BUNDLE_WITH: maintenance 22 | 23 | jobs: 24 | generate: 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | 30 | - uses: ruby/setup-ruby@v1 31 | with: 32 | ruby-version: "3.3" 33 | bundler-cache: true 34 | 35 | - name: Installing packages 36 | run: sudo apt-get install wget 37 | 38 | - name: Generate documentation 39 | timeout-minutes: 5 40 | run: bundle exec bake utopia:project:static --force no 41 | 42 | - name: Upload documentation artifact 43 | uses: actions/upload-pages-artifact@v2 44 | with: 45 | path: docs 46 | 47 | deploy: 48 | runs-on: ubuntu-latest 49 | 50 | environment: 51 | name: github-pages 52 | url: ${{steps.deployment.outputs.page_url}} 53 | 54 | needs: generate 55 | steps: 56 | - name: Deploy to GitHub Pages 57 | id: deployment 58 | uses: actions/deploy-pages@v3 59 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | env: 9 | CONSOLE_OUTPUT: XTerm 10 | 11 | jobs: 12 | test: 13 | name: ${{matrix.ruby}} on ${{matrix.os}} 14 | runs-on: ${{matrix.os}}-latest 15 | continue-on-error: ${{matrix.experimental}} 16 | 17 | strategy: 18 | matrix: 19 | os: 20 | - ubuntu 21 | - macos 22 | 23 | ruby: 24 | - "3.1" 25 | - "3.2" 26 | - "3.3" 27 | 28 | experimental: [false] 29 | 30 | include: 31 | - os: ubuntu 32 | ruby: truffleruby 33 | experimental: true 34 | - os: ubuntu 35 | ruby: jruby 36 | experimental: true 37 | - os: ubuntu 38 | ruby: head 39 | experimental: true 40 | 41 | steps: 42 | - uses: actions/checkout@v4 43 | - uses: ruby/setup-ruby@v1 44 | with: 45 | ruby-version: ${{matrix.ruby}} 46 | bundler-cache: true 47 | 48 | - name: Installing packages (ubuntu) 49 | if: matrix.os == 'ubuntu' 50 | run: sudo apt-get install wget 51 | 52 | - name: Installing packages (macos) 53 | if: matrix.os == 'macos' 54 | run: brew install wget 55 | 56 | - name: Run tests 57 | timeout-minutes: 10 58 | run: bundle exec bake test 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /pkg/ 3 | /gems.locked 4 | /.covered.db 5 | /external 6 | 7 | /.github/workflows/test-external.yaml 8 | /test/.static 9 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Dave Wilkinson 2 | -------------------------------------------------------------------------------- /bin/rackula: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Copyright, 2017, by Samuel G. D. Williams. 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | require 'rackula/command' 24 | 25 | Rackula::Command.call 26 | -------------------------------------------------------------------------------- /config/sus.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2024, by Samuel Williams. 5 | 6 | require 'covered/sus' 7 | include Covered::Sus 8 | -------------------------------------------------------------------------------- /gems.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2017-2024, by Samuel Williams. 5 | 6 | source 'https://rubygems.org' 7 | 8 | gemspec 9 | 10 | group :maintenance, optional: true do 11 | gem "bake-gem" 12 | gem "bake-modernize" 13 | 14 | gem "utopia-project" 15 | end 16 | 17 | group :test do 18 | gem "sus" 19 | gem "covered" 20 | 21 | gem "bake-test" 22 | gem "bake-test-external" 23 | end 24 | -------------------------------------------------------------------------------- /guides/getting-started/readme.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | This guide will show you how to use the `rackula` gem to create a simple static website. 4 | 5 | ## Installation 6 | 7 | Add the gem to your project: 8 | 9 | ~~~ bash 10 | $ bundle add rackula 11 | ~~~ 12 | 13 | and make sure `wget` is also installed, using your system package manager. 14 | 15 | ## Usage 16 | 17 | ### Rack Applications 18 | 19 | In the root directory, simply run `rackula`. It will generate a static site in `static`. For more details about how to change the default behavior, run `rackula --help`. 20 | 21 | ### Rails Applications 22 | 23 | Add a `config.ru` file to your rails app and follow the above instructions. 24 | 25 | ``` ruby 26 | # config.ru for rails app 27 | require_relative 'config/environment' 28 | run Rails.application 29 | ``` 30 | -------------------------------------------------------------------------------- /guides/links.yaml: -------------------------------------------------------------------------------- 1 | getting-started: 2 | order: 1 3 | -------------------------------------------------------------------------------- /lib/rackula.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2017-2024, by Samuel Williams. 5 | 6 | require "rackula/version" 7 | -------------------------------------------------------------------------------- /lib/rackula/command.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2017-2024, by Samuel Williams. 5 | 6 | require_relative 'version' 7 | 8 | require_relative 'command/generate' 9 | 10 | module Rackula 11 | module Command 12 | def self.call(*args) 13 | Top.call(*args) 14 | end 15 | 16 | # The top level utopia command. 17 | class Top < Samovar::Command 18 | self.description = "A static site generation tool." 19 | 20 | options do 21 | option '-i/--in/--root ', "Work in the given root directory." 22 | option '-h/--help', "Print out help information." 23 | option '-v/--version', "Print out the application version." 24 | end 25 | 26 | nested :command, { 27 | 'generate' => Generate 28 | }, default: 'generate' 29 | 30 | # The root directory for the site. 31 | def root 32 | File.expand_path(@options.fetch(:root, ''), Dir.getwd) 33 | end 34 | 35 | def call 36 | if @options[:version] 37 | puts "#{self.name} v#{VERSION}" 38 | elsif @options[:help] 39 | print_usage(output: $stdout) 40 | else 41 | @command.call 42 | end 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/rackula/command/generate.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2017-2024, by Samuel Williams. 5 | # Copyright, 2018, by Dave Wilkinson. 6 | 7 | require 'samovar' 8 | 9 | require 'pathname' 10 | require 'fileutils' 11 | require 'rack' 12 | 13 | require 'falcon/server' 14 | 15 | require 'io/endpoint/host_endpoint' 16 | require 'async/container' 17 | require 'async/http' 18 | 19 | require 'variant' 20 | 21 | module Rackula 22 | module Command 23 | # Server setup commands. 24 | class Generate < Samovar::Command 25 | self.description = "Start a local server and generate a static version of a site." 26 | 27 | options do 28 | option '-c/--config ', "Rackup configuration file to load.", default: 'config.ru' 29 | option '-p/--public ', "The public path to copy initial files from.", default: 'public' 30 | option '-o/--output-path ', "The output path to save static site.", default: 'static' 31 | 32 | option '-f/--force', "If the output path exists, delete it.", default: false 33 | end 34 | 35 | def copy_and_fetch(port, root) 36 | output_path = File.expand_path(@options[:output_path], root) 37 | 38 | if File.exist?(output_path) 39 | if @options[:force] 40 | # Delete any existing stuff: 41 | FileUtils.rm_rf(output_path.to_s) 42 | end 43 | end 44 | 45 | # Create output directory 46 | FileUtils.mkdir_p(output_path) 47 | 48 | # Copy all public assets: 49 | asset_pattern = root + @options[:public] + '*' 50 | Dir.glob(asset_pattern.to_s).each do |path| 51 | FileUtils.cp_r(path, File.join(output_path, ".")) 52 | end 53 | 54 | # Generate HTML pages: 55 | unless system("wget", "--mirror", "--recursive", "--convert-links", "--adjust-extension", "--no-host-directories", "--directory-prefix", output_path.to_s, "http://localhost:#{port}") 56 | raise "The wget command failed!" 57 | end 58 | end 59 | 60 | def serve(endpoint, root) 61 | container = Async::Container.new 62 | 63 | config_path = root + @options[:config] 64 | 65 | container.run do |instance| 66 | Sync do 67 | rack_app, _ = Rack::Builder.parse_file(config_path.to_s) 68 | app = ::Falcon::Server.middleware(rack_app, verbose: @options[:verbose]) 69 | server = ::Falcon::Server.new(app, endpoint) 70 | 71 | instance.ready! 72 | 73 | server.run 74 | end 75 | end 76 | 77 | container.wait_until_ready 78 | 79 | return container 80 | end 81 | 82 | def run(bound_endpoint, root) 83 | # We need to determine the actual port we are bound to: 84 | local_addresses = bound_endpoint.sockets.map(&:local_address) 85 | address = local_addresses.first 86 | 87 | endpoint = Async::HTTP::Endpoint.parse("http://localhost", bound_endpoint) 88 | 89 | Console.logger.info(self) {"Setting up container to serve site on port #{address.ip_port}..."} 90 | container = serve(endpoint, root) 91 | 92 | Console.logger.info(self) {"Copy and fetch site to static..."} 93 | copy_and_fetch(address.ip_port, root) 94 | ensure 95 | container&.stop 96 | end 97 | 98 | def call 99 | Variant.force!('static') 100 | 101 | endpoint = ::IO::Endpoint.tcp("localhost", 0, reuse_port: true) 102 | 103 | begin 104 | bound_endpoint = endpoint.bound 105 | 106 | run(bound_endpoint, Pathname.new(parent.root)) 107 | ensure 108 | bound_endpoint&.close 109 | end 110 | end 111 | end 112 | end 113 | end 114 | -------------------------------------------------------------------------------- /lib/rackula/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2017-2024, by Samuel Williams. 5 | 6 | module Rackula 7 | VERSION = "1.4.1" 8 | end 9 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright, 2017-2024, by Samuel Williams. 4 | Copyright, 2018, by Dave Wilkinson. 5 | Copyright, 2020, by Olle Jonsson. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /rackula.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "lib/rackula/version" 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "rackula" 7 | spec.version = Rackula::VERSION 8 | 9 | spec.summary = "Generate a static site from any rackup compatible application." 10 | spec.authors = ["Samuel Williams", "Dave Wilkinson", "Olle Jonsson"] 11 | spec.license = "MIT" 12 | 13 | spec.cert_chain = ['release.cert'] 14 | spec.signing_key = File.expand_path('~/.gem/release.pem') 15 | 16 | spec.homepage = "https://github.com/socketry/rackula" 17 | 18 | spec.metadata = { 19 | "documentation_uri" => "https://socketry.github.io/rackula/", 20 | "source_code_uri" => "https://github.com/socketry/rackula.git", 21 | } 22 | 23 | spec.files = Dir.glob(['{bin,lib}/**/*', '*.md'], File::FNM_DOTMATCH, base: __dir__) 24 | 25 | spec.executables = ["rackula"] 26 | 27 | spec.required_ruby_version = ">= 3.1" 28 | 29 | spec.add_dependency "falcon", "~> 0.46" 30 | spec.add_dependency "samovar", "~> 2.1" 31 | spec.add_dependency "variant" 32 | end 33 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Rackula 2 | 3 | Rackula will immortalize your rackup web app by generating a static copy. It can be used to generate a static site from any rack-compatible middleware (e.g. rails, sinatra, utopia). 4 | 5 | [![Development Status](https://github.com/socketry/rackula/workflows/Test/badge.svg)](https://github.com/socketry/rackula/actions?workflow=Test) 6 | 7 | ## Usage 8 | 9 | Please see the [project documentation](https://socketry.github.io/rackula/) for more details. 10 | 11 | - [Getting Started](https://socketry.github.io/rackula/guides/getting-started/index) - This guide will show you how to use the `rackula` gem to create a simple static website. 12 | 13 | ## Contributing 14 | 15 | We welcome contributions to this project. 16 | 17 | 1. Fork it. 18 | 2. Create your feature branch (`git checkout -b my-new-feature`). 19 | 3. Commit your changes (`git commit -am 'Add some feature'`). 20 | 4. Push to the branch (`git push origin my-new-feature`). 21 | 5. Create new Pull Request. 22 | 23 | ### Developer Certificate of Origin 24 | 25 | This project uses the [Developer Certificate of Origin](https://developercertificate.org/). All contributors to this project must agree to this document to have their contributions accepted. 26 | 27 | ### Contributor Covenant 28 | 29 | This project is governed by the [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms. 30 | -------------------------------------------------------------------------------- /release.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11 3 | ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK 4 | CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz 5 | MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd 6 | MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj 7 | bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB 8 | igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2 9 | 9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW 10 | sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE 11 | e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN 12 | XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss 13 | RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn 14 | tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM 15 | zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW 16 | xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O 17 | BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs 18 | aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs 19 | aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE 20 | cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl 21 | xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/ 22 | c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp 23 | 8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws 24 | JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP 25 | eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt 26 | Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8 27 | voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg= 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /test/.site/another.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Another 5 | 6 | 7 |

Another page, for another day

8 | 9 | -------------------------------------------------------------------------------- /test/.site/config.ru: -------------------------------------------------------------------------------- 1 | 2 | require 'rack' 3 | 4 | run Proc.new { |env| 5 | # Extract the requested path from the request 6 | path = env["PATH_INFO"] 7 | path = "index.html" if path == "/" 8 | 9 | full_path = File.join(__dir__, path) 10 | 11 | if File.exist?(full_path) 12 | [200, {}, [File.read(full_path)]] 13 | else 14 | [404, {}, []] 15 | end 16 | } -------------------------------------------------------------------------------- /test/.site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Index 5 | 6 | 7 | another 8 | 9 | -------------------------------------------------------------------------------- /test/.site/public/document.txt: -------------------------------------------------------------------------------- 1 | Test document. 2 | -------------------------------------------------------------------------------- /test/rakula.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Released under the MIT License. 4 | # Copyright, 2017-2024, by Samuel Williams. 5 | # Copyright, 2018, by Dave Wilkinson. 6 | 7 | require 'rackula/command' 8 | 9 | describe Rackula do 10 | let(:root) {File.expand_path(".site/", __dir__)} 11 | let(:output_path) {File.expand_path(".static/", __dir__)} 12 | 13 | it "can generate copy of site" do 14 | Rackula::Command::Top["--root", root, "generate", "--force", "--output-path", output_path].call 15 | 16 | expect(File).to be(:exist?, File.join(output_path, "index.html")) 17 | expect(File).to be(:exist?, File.join(output_path, "another.html")) 18 | expect(File).to be(:exist?, File.join(output_path, "document.txt")) 19 | end 20 | 21 | it "can generate copy of site into existing directory" do 22 | FileUtils.mkdir_p(output_path) 23 | FileUtils.touch(File.join(output_path, "existing.txt")) 24 | 25 | Rackula::Command::Top["--root", root, "generate", "--output-path", output_path].call 26 | 27 | expect(File).to be(:exist?, File.join(output_path, "index.html")) 28 | expect(File).to be(:exist?, File.join(output_path, "another.html")) 29 | expect(File).to be(:exist?, File.join(output_path, "document.txt")) 30 | 31 | expect(File).to be(:exist?, File.join(output_path, "existing.txt")) 32 | end 33 | end 34 | --------------------------------------------------------------------------------