├── .gitignore ├── assets ├── rack │ └── config.ru └── css │ └── annyong.css ├── LICENSE ├── lib ├── annyong.rb └── annyong │ └── directory.rb ├── README.mkd ├── annyong.gemspec └── bin └── annyong /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .rvmrc 3 | .local* 4 | -------------------------------------------------------------------------------- /assets/rack/config.ru: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | run Annyong::Directory.new(FileUtils.pwd.chomp) 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2011 Rémi Prévost. 2 | You may use this work without restrictions, as long as this notice is included. 3 | The work is provided "as is" without warranty of any kind, neither express nor implied. 4 | -------------------------------------------------------------------------------- /lib/annyong.rb: -------------------------------------------------------------------------------- 1 | module Annyong 2 | VERSION = '0.4' 3 | autoload :Directory, "annyong/directory" 4 | end 5 | 6 | unless "".respond_to?(:each) 7 | String.class_eval do 8 | def each &block 9 | self.lines &block 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /README.mkd: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Annyong starts a public static Web server in the current directory, allowing people in your local network to browse and download your files. 4 | 5 | ![Screenshot of Annyong in action](http://i.imgur.com/zRg9Y.png) 6 | 7 | ## Usage 8 | 9 | $ gem install annyong 10 | $ cd /path/you/want/to/serve 11 | $ annyong 12 | 13 | ## Options 14 | 15 | * `--port [PORT]` → the port to use (default: 9292) 16 | * `--host [HOST]` → the host to listen on (default: 0.0.0.0) 17 | 18 | ## Thanks 19 | 20 | * [Doug McInnes](https://github.com/dmcinnes) (to prevent injecting annyong’s code into public HTML files) 21 | -------------------------------------------------------------------------------- /annyong.gemspec: -------------------------------------------------------------------------------- 1 | spec = Gem::Specification.new do |s| 2 | s.name = "annyong" 3 | s.version = "0.4" 4 | s.platform = Gem::Platform::RUBY 5 | s.authors = "Rémi Prévost" 6 | s.email = "remi@exomel.com" 7 | s.homepage = "http://github.com/remiprev/annyong" 8 | s.summary = "Start a public static Web server in the current directory." 9 | s.description = "Annyong starts a public static Web server in the current directory, allowing people in your local network to browse your files." 10 | s.files = Dir["lib/**/*.rb", "README.mkd", "LICENSE", "bin/*", "assets/**/*"] 11 | s.require_path = "lib" 12 | 13 | s.executables << "annyong" 14 | s.add_runtime_dependency "rack", [">= 1.1.0"] 15 | end 16 | -------------------------------------------------------------------------------- /bin/annyong: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib]) 4 | 5 | require "rubygems" 6 | require "fileutils" 7 | require "optparse" 8 | require "rack" 9 | require "annyong" 10 | 11 | options = { 12 | :port => 9292, 13 | :host => "0.0.0.0" 14 | } 15 | opts = OptionParser.new do |opts| 16 | opts.banner = "Usage: annyong [options] 17 | 18 | Options: 19 | " 20 | opts.on("--port [PORT]", "The port to use (default: 9292)") do |port| 21 | options[:port] = port 22 | end 23 | 24 | opts.on("--host [HOST]", "The host to listen on (default: 0.0.0.0)") do |host| 25 | options[:host] = host 26 | end 27 | 28 | #opts.on("--path [PATH]", "The directory to serve") do |path| 29 | #options[:path] = path 30 | #end 31 | 32 | end 33 | opts.parse! 34 | 35 | STDOUT.puts "Now serving files in #{FileUtils.pwd.chomp} on http://#{options[:host]}:#{options[:port]}. Annyong!" 36 | 37 | Rack::Server.start({ 38 | :config => File.join(File.dirname(__FILE__), "../assets/rack/config.ru"), 39 | :Port => options[:port], 40 | :Host => options[:host], 41 | :AccessLog => [], 42 | }) 43 | -------------------------------------------------------------------------------- /lib/annyong/directory.rb: -------------------------------------------------------------------------------- 1 | module Annyong 2 | class Directory < Rack::Directory 3 | DIR_FILE = "%s%s%s%s" 4 | DIR_PAGE = <<-PAGE 5 | 6 | 7 | %s 8 | 9 | 10 | 13 | 14 |

%s

15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | %s 23 |
NameSizeTypeLast Modified
24 | 25 | 26 | PAGE 27 | 28 | def each 29 | page = DIR_PAGE % [html_title, page_title, files] 30 | page.each_line { |content| yield content } 31 | end 32 | 33 | private 34 | 35 | def html_title 36 | path = @path.sub(/^#{@root}/,'').gsub(/\/$/, '') 37 | File.basename(FileUtils.pwd) + path 38 | end 39 | 40 | def page_title 41 | parts = html_title.split('/') 42 | parts.each_with_index.map do |item, index| 43 | "#{item}" 44 | end.join(' / ') 45 | end 46 | 47 | def files 48 | @files.map { |file| DIR_FILE % file } * "\n" 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /assets/css/annyong.css: -------------------------------------------------------------------------------- 1 | table { width:100%; } 2 | .name { text-align:left; } 3 | .size, .mtime { text-align:right; } 4 | .type { width:11em; } 5 | .mtime { width:15em; } 6 | 7 | /*=============================================================== 8 | Basic 9 | ===============================================================*/ 10 | 11 | * { 12 | padding: 0; 13 | margin: 0; 14 | } 15 | 16 | html { 17 | font-size: 62.5%; 18 | } 19 | 20 | body { 21 | font-family: Helvetica, Arial, sans-serif; 22 | white-space: nowrap; 23 | font-size: 145%; 24 | padding: 30px; 25 | background: #fff; 26 | color: #555; 27 | } 28 | 29 | a { 30 | color: #508AD7; 31 | } 32 | 33 | hr { 34 | display: none; 35 | } 36 | 37 | h1 { 38 | margin: 0 0 15px; 39 | font-size: 150%; 40 | } 41 | 42 | /*=============================================================== 43 | Tables 44 | ===============================================================*/ 45 | 46 | table { 47 | border-collapse: collapse; 48 | background: #fff; 49 | box-shadow: 0 0 10px rgba(0,0,0,0.1); 50 | background: #fafafa; 51 | border: 1px solid #ccc; 52 | font-family: monospace; 53 | } 54 | 55 | table tr:nth-child(2) { 56 | background: #fafafa !important; 57 | } 58 | 59 | table tr:nth-child(2) a { 60 | color: #999; 61 | } 62 | 63 | table td, table th { 64 | border-bottom: 1px solid #ccc; 65 | padding: 6px 8px; 66 | width: auto; 67 | font-weight: normal; 68 | text-align: left; 69 | } 70 | 71 | table th { 72 | background: #eee; 73 | color: #888; 74 | } 75 | 76 | table td.name { 77 | padding: 0; 78 | } 79 | 80 | table td.name a { 81 | display: block; 82 | padding: 6px 8px; 83 | } 84 | 85 | table tr:hover { 86 | background: rgba(255,255,0,0.1); 87 | } 88 | 89 | table td.name { 90 | } 91 | 92 | /*=============================================================== 93 | Footer 94 | ===============================================================*/ 95 | 96 | footer { 97 | padding: 16px 2px 6px; 98 | text-align: right; 99 | font-size: 90%; 100 | color: #999; 101 | } 102 | 103 | footer a { 104 | color: #aaa; 105 | } 106 | 107 | /*=============================================================== 108 | Mobile 109 | ===============================================================*/ 110 | 111 | @media only screen and (max-width : 520px) { 112 | body { 113 | padding: 15px; 114 | font-size: 165%; 115 | } 116 | 117 | table td.name a { 118 | padding: 10px 12px; 119 | } 120 | 121 | table td.type, table th.type, table td.size, table th.size, table td.mtime, table th.mtime { 122 | display: none; 123 | } 124 | } 125 | --------------------------------------------------------------------------------