Module: Jimmu::CLI

Defined in:
lib/jimmu.rb

Constant Summary collapse

TEMPLATE_APP_RB =
<<~'RUBY'
  # Jimmu application entry point.
  # Start the development server with:  jimmu server

  port 3000
  host "127.0.0.1"

  before do
    log "#{request.method} #{request.path}"
  end

  run
RUBY
TEMPLATE_LAYOUT_ERB =
<<~'ERB'
  <!DOCTYPE html>
  <html lang="en">
  <head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Jimmu App</title>
  <link rel="stylesheet" href="/css/style.css">
  </head>
  <body>
  <%= yield %>
  <script src="/js/app.js"></script>
  </body>
  </html>
ERB
TEMPLATE_INDEX_ERB =
<<~'ERB'
  <h1>Welcome to Jimmu</h1>
  <p>Edit <code>views/index.erb</code> to get started.</p>
  <p>This page is automatically wrapped by <code>views/layout.erb</code>.</p>
ERB
TEMPLATE_404_ERB =
<<~'ERB'
  <h1>404 - Page Not Found</h1>
  <p>The page you requested does not exist.</p>
  <p><a href="/">Go back home</a></p>
ERB
TEMPLATE_500_ERB =
<<~'ERB'
  <h1>500 - Internal Server Error</h1>
  <p>Something went wrong on our end.</p>
ERB
TEMPLATE_CSS =
<<~'CSS'
  :root { color-scheme: light dark; }
  * { box-sizing: border-box; }
  body {
    margin: 0;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
    line-height: 1.6;
    max-width: 760px;
    padding: 48px 24px;
    margin: 0 auto;
  }
  code {
    background: rgba(127,127,127,0.15);
    padding: 2px 6px;
    border-radius: 4px;
    font-size: 0.9em;
  }
  a { color: #3b6fe0; }
CSS
TEMPLATE_JS =
<<~'JS'
  console.log("Jimmu app loaded");
JS

Class Method Summary collapse

Class Method Details

.cmd_new(args) ⇒ Object



1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
# File 'lib/jimmu.rb', line 1828

def self.cmd_new(args)
  name = args[0]
  if name.nil? || name.strip.empty?
    warn 'Usage: jimmu new <name>'
    exit(1)
  end

  root = File.expand_path(name)
  if Dir.exist?(root) && !Dir.empty?(root)
    warn "Error: directory #{name.inspect} already exists and is not empty."
    exit(1)
  end

  %w[views public/css public/js public/img db].each do |dir|
    FileUtils.mkdir_p(File.join(root, dir))
  end

  write_file(File.join(root, 'app.rb'), TEMPLATE_APP_RB)
  write_file(File.join(root, 'views/layout.erb'), TEMPLATE_LAYOUT_ERB)
  write_file(File.join(root, 'views/index.erb'), TEMPLATE_INDEX_ERB)
  write_file(File.join(root, 'views/404.erb'), TEMPLATE_404_ERB)
  write_file(File.join(root, 'views/500.erb'), TEMPLATE_500_ERB)
  write_file(File.join(root, 'public/css/style.css'), TEMPLATE_CSS)
  write_file(File.join(root, 'public/js/app.js'), TEMPLATE_JS)
  write_file(File.join(root, 'public/img/.gitkeep'), '')
  write_file(File.join(root, 'db/.gitkeep'), '')

  puts "Created new Jimmu app in ./#{name}"
  puts ''
  puts '  cd ' + name
  puts '  jimmu server'
  puts ''
end

.cmd_server(args) ⇒ Object



1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
# File 'lib/jimmu.rb', line 1866

def self.cmd_server(args)
  forced_port = nil
  forced_host = nil

  parser = OptionParser.new do |opts|
    opts.banner = 'Usage: jimmu server [options]'
    opts.on('-p PORT', '--port PORT', 'Port to listen on') { |v| forced_port = v }
    opts.on('-h HOST', '--host HOST', 'Host/IP to bind to') { |v| forced_host = v }
  end

  begin
    parser.parse!(args)
  rescue OptionParser::ParseError => e
    warn "Error: #{e.message}"
    exit(1)
  end

  app_rb_path = File.expand_path('app.rb', Dir.pwd)
  unless File.file?(app_rb_path)
    warn "Error: no app.rb found in the current directory (#{Dir.pwd})."
    warn 'Run `jimmu new <name>` to create a new project, or `cd` into an existing one.'
    exit(1)
  end

  app = Jimmu::Application.new(Dir.pwd)
  app.force_port!(forced_port) if forced_port
  app.force_host!(forced_host) if forced_host

  begin
    app.load_app_file(app_rb_path)
    unless app.run_called?
      app.log('app.rb did not call `run` -- starting the server automatically.')
      app.run
    end
  rescue Jimmu::ConfigError => e
    warn "Error: #{e.message}"
    exit(1)
  rescue Jimmu::DatabaseError => e
    warn "Database error: #{e.message}"
    exit(1)
  rescue Interrupt
    puts "\nShutting down..."
    exit(0)
  rescue Exception => e
    warn "Error loading app.rb: #{e.class}: #{e.message}"
    (e.backtrace || []).first(15).each { |l| warn "    #{l}" }
    exit(1)
  end
end


1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
# File 'lib/jimmu.rb', line 1804

def self.print_help
  puts <<~HELP
    Jimmu #{Jimmu::VERSION} - a lightweight, single-file Ruby web framework

    Usage:
      jimmu new <name>        Create a new Jimmu project in ./<name>
      jimmu server [options]  Start the development server (reads ./app.rb)
      jimmu version           Print the installed Jimmu version
      jimmu help              Show this help message
      jimmu build              (reserved for future use)

    Server options:
      -p, --port PORT   Port to listen on (overrides app.rb's `port`, default 3000)
      -h, --host HOST   Host/IP to bind to (overrides app.rb's `host`, default 127.0.0.1)

    Examples:
      jimmu new myapp
      cd myapp
      jimmu server
      jimmu server -p 8080
      jimmu server -h 0.0.0.0 -p 8080
  HELP
end

.start(argv) ⇒ Object

entry point =================


1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
# File 'lib/jimmu.rb', line 1783

def self.start(argv)
  command = argv[0]
  case command
  when 'new'
    cmd_new(argv[1..-1])
  when 'server'
    cmd_server(argv[1..-1])
  when 'version', '-v', '--version'
    puts "jimmu #{Jimmu::VERSION}"
  when 'build'
    puts 'jimmu build: not implemented yet. Future versions of Jimmu will support'
    puts 'bundling an app into a single distributable executable/package.'
  when 'help', '-h', '--help', nil
    print_help
  else
    warn "Unknown command: #{command}\n\n"
    print_help
    exit(1)
  end
end

.write_file(path, content) ⇒ Object



1862
1863
1864
# File 'lib/jimmu.rb', line 1862

def self.write_file(path, content)
  File.write(path, content, encoding: Encoding::UTF_8)
end