servel 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90781daa208037a411ff728a5b24119dd2680e83
4
- data.tar.gz: 5fda6f639a3b61fe93054b69075f552070b68ab8
3
+ metadata.gz: f6492d021105b80fbde26cbc28be8ae3e94e3363
4
+ data.tar.gz: eef6c59037a02a8773e757f590a1e421dcd227cc
5
5
  SHA512:
6
- metadata.gz: 44b15921dd110dc9d6dd139f0f748f94bacc7d16ec37b8a398f48a6e643b11a7222fb148b9fa4955576aadda38d7376d8afca775fefdd8b05291ce061bb8ffd8
7
- data.tar.gz: beb51c3c7ff7f278119d6430482e3d506984ffec030dd7bdbfe61872275b8b3bfdcfdbc7ec4e4fe39c8e15122ed037754c129da5ad9de6df994c8e0167d187e7
6
+ metadata.gz: 4d9562a78fc9bde9928591f32301b20a0b3e63bd74cb8c41c4f90cfb4610d5c31a2cc065906e94393b40836c2ca2a8c92c3e2a34bcfeb41672e9806fa162e6f6
7
+ data.tar.gz: 891d91db9bef20c5b34fbf34dc2e8abf93aca9a09e400795c561c0dfa3d539cd7a06f2170b5ddb16bd55eb642ed81b301d07262914a9b39756b207dea565f41b
data/bin/servel CHANGED
@@ -2,5 +2,4 @@
2
2
 
3
3
  require "servel"
4
4
 
5
- servel = Servel::Servel.new(Pathname.new(ARGV.first).realpath)
6
- servel.start
5
+ Servel::CLI.new(ARGV).start
@@ -9,12 +9,18 @@ require 'pathname'
9
9
  require 'delegate'
10
10
 
11
11
  module Servel
12
+ def self.build_app(path_map)
13
+ url_map = path_map.map { |root, url_root| [url_root, Servel::App.new(root)] }.to_h
14
+ url_map["/"] = Servel::HomeApp.new(path_map.values) unless url_map.keys.include?("/")
15
+
16
+ Rack::URLMap.new(url_map)
17
+ end
12
18
  end
13
19
 
14
20
  require "servel/version"
15
- require "servel/core_ext/pathname"
16
21
  require "servel/pathname_decorator"
17
22
  require "servel/haml_context"
18
23
  require "servel/locals"
19
- require "servel/middleware"
20
- require "servel/servel"
24
+ require "servel/app"
25
+ require "servel/home_app"
26
+ require "servel/cli"
@@ -0,0 +1,38 @@
1
+ class Servel::App
2
+ def initialize(root)
3
+ @root = Pathname.new(root)
4
+ @file_server = Rack::File.new(root.to_s)
5
+ end
6
+
7
+ def call(env)
8
+ url_root = env["SCRIPT_NAME"]
9
+ url_path = clean_url_path(env["PATH_INFO"])
10
+
11
+ return redirect("#{url_root}/") if env["PATH_INFO"] == ""
12
+
13
+ fs_path = @root + url_path[1..-1]
14
+
15
+ return @file_server.call(env) if fs_path.file?
16
+
17
+ return redirect("#{url_root}#{url_path}/") unless env["PATH_INFO"].end_with?("/")
18
+
19
+ index(Servel::Locals.new(url_root: url_root, url_path: url_path, fs_path: fs_path))
20
+ end
21
+
22
+ def redirect(location)
23
+ [302, { "Location" => location }, []]
24
+ end
25
+
26
+ def clean_url_path(path)
27
+ url_path = Rack::Utils.unescape_path(path)
28
+ raise unless Rack::Utils.valid_path?(url_path)
29
+ Rack::Utils.clean_path_info(url_path)
30
+ end
31
+
32
+ def index(locals)
33
+ @haml_context ||= Servel::HamlContext.new
34
+ body = @haml_context.render('index.haml', locals.resolve)
35
+
36
+ [200, {}, [body]]
37
+ end
38
+ end
@@ -0,0 +1,18 @@
1
+ class Servel::CLI
2
+ def initialize(argv)
3
+ @argv = argv
4
+ end
5
+
6
+ def start
7
+ Rack::Handler::Puma.run(Servel.build_app(path_map))
8
+ end
9
+
10
+ def path_map
11
+ @argv.map do |arg|
12
+ root, url_root = arg.split(":" , 2)
13
+ root = Pathname.new(root).realpath
14
+
15
+ [root, url_root || "/"]
16
+ end.to_h
17
+ end
18
+ end
@@ -20,6 +20,10 @@ class Servel::HamlContext
20
20
  (@build_path + path).read
21
21
  end
22
22
 
23
+ def decorate(path)
24
+ Servel::PathnameDecorator.new(pathname: path)
25
+ end
26
+
23
27
  def haml_engine(path)
24
28
  unless @haml_engine_cache.key?(path)
25
29
  @haml_engine_cache[path] = Haml::Engine.new(include(path), ENGINE_OPTIONS.merge(filename: path))
@@ -0,0 +1,12 @@
1
+ class Servel::HomeApp
2
+ def initialize(roots)
3
+ @roots = roots
4
+ end
5
+
6
+ def call(env)
7
+ @haml_context ||= Servel::HamlContext.new
8
+ body = @haml_context.render('home.haml', { roots: @roots })
9
+
10
+ [200, {}, [body]]
11
+ end
12
+ end
@@ -1,12 +1,13 @@
1
1
  class Servel::Locals
2
- def initialize(url_path:, fs_path:, root:)
2
+ def initialize(url_root:, url_path:, fs_path:)
3
+ @url_root = url_root
3
4
  @url_path = url_path
4
5
  @fs_path = fs_path
5
- @root = root
6
6
  end
7
7
 
8
- def locals
8
+ def resolve
9
9
  {
10
+ url_root: @url_root,
10
11
  url_path: @url_path,
11
12
  directories: directories,
12
13
  files: files
@@ -16,7 +17,12 @@ class Servel::Locals
16
17
  def directories
17
18
  list = @fs_path.children.select { |child| child.directory? }
18
19
  list = sort_paths(list)
19
- list.unshift(@fs_path.decorate(true)) unless @fs_path == @root
20
+
21
+ unless @url_path == "/"
22
+ list.unshift(Servel::PathnameDecorator.new(pathname: "../", parent: true))
23
+ list.unshift(Servel::PathnameDecorator.new(pathname: @url_root, top: true))
24
+ end
25
+
20
26
  list
21
27
  end
22
28
 
@@ -1,91 +1,110 @@
1
- class Servel::PathnameDecorator < SimpleDelegator
2
- def initialize(pathname, parent)
3
- super(pathname)
4
- @parent = parent
5
- end
6
-
7
- def decorate
8
- self
9
- end
10
-
11
- def image?
12
- file? && extname && %w(.jpg .jpeg .png .gif).include?(extname.downcase)
13
- end
14
-
15
- def video?
16
- file? && extname && %w(.webm).include?(extname.downcase)
17
- end
18
-
19
- def audio?
20
- file? && extname && %w(.mp3 .m4a .wav).include?(extname.downcase)
21
- end
22
-
23
- def media?
24
- image? || video? || audio?
25
- end
26
-
27
- def type
28
- if directory?
29
- "Dir"
30
- elsif file?
31
- extname.sub(/^\./, "")
32
- else
33
- ""
34
- end
35
- end
36
-
37
- def media_type
38
- return "video" if video?
39
- return "image" if image?
40
- return "audio" if audio?
41
- "unknown"
42
- end
43
-
44
- def listing_classes
45
- klasses = []
46
- klasses << "media" if media?
47
- klasses << "image" if image?
48
- klasses << "video" if video?
49
- klasses << "audio" if audio?
50
- klasses.join(" ")
51
- end
52
-
53
- def listing_attrs
54
- {
55
- class: listing_classes,
56
- data: {
57
- type: media_type
58
- }
59
- }
60
- end
61
-
62
- def parent?
63
- @parent
64
- end
65
-
66
- def icon
67
- if @parent
68
- "🔝"
69
- elsif directory?
70
- "📁"
71
- else
72
- ""
73
- end
74
- end
75
-
76
- def href
77
- if @parent
78
- "../"
79
- else
80
- basename
81
- end
82
- end
83
-
84
- def name
85
- if @parent
86
- "(Parent Directory)"
87
- else
88
- basename
89
- end
90
- end
1
+ class Servel::PathnameDecorator < SimpleDelegator
2
+ def initialize(pathname:, parent: false, top: false)
3
+ super(pathname)
4
+ @parent = parent
5
+ @top = top
6
+ end
7
+
8
+ def image?
9
+ file? && extname && %w(.jpg .jpeg .png .gif).include?(extname.downcase)
10
+ end
11
+
12
+ def video?
13
+ file? && extname && %w(.webm .mp4).include?(extname.downcase)
14
+ end
15
+
16
+ def audio?
17
+ file? && extname && %w(.mp3 .m4a .wav).include?(extname.downcase)
18
+ end
19
+
20
+ def media?
21
+ image? || video? || audio?
22
+ end
23
+
24
+ def type
25
+ if directory?
26
+ "Dir"
27
+ elsif file?
28
+ extname.sub(/^\./, "")
29
+ else
30
+ ""
31
+ end
32
+ end
33
+
34
+ def media_type
35
+ return "video" if video?
36
+ return "image" if image?
37
+ return "audio" if audio?
38
+ "unknown"
39
+ end
40
+
41
+ def listing_classes
42
+ klasses = []
43
+ klasses << "media" if media?
44
+ klasses << "image" if image?
45
+ klasses << "video" if video?
46
+ klasses << "audio" if audio?
47
+ klasses << "top" if top?
48
+ klasses << "parent" if parent?
49
+ klasses << "file" if file?
50
+ klasses << "directory" if directory?
51
+ klasses.join(" ")
52
+ end
53
+
54
+ def listing_attrs
55
+ {
56
+ class: listing_classes,
57
+ data: {
58
+ type: media_type
59
+ }
60
+ }
61
+ end
62
+
63
+ def top?
64
+ @top
65
+ end
66
+
67
+ def parent?
68
+ @parent
69
+ end
70
+
71
+ def virtual?
72
+ top? || parent?
73
+ end
74
+
75
+ def icon
76
+ if @top
77
+ "🔝"
78
+ elsif @parent
79
+ "⬆️"
80
+ elsif directory?
81
+ "📁"
82
+ elsif video?
83
+ "🎞️"
84
+ elsif image?
85
+ "🖼️"
86
+ elsif audio?
87
+ "🔊"
88
+ else
89
+ "📝"
90
+ end
91
+ end
92
+
93
+ def href
94
+ if @top || @parent
95
+ to_s
96
+ else
97
+ basename
98
+ end
99
+ end
100
+
101
+ def name
102
+ if @top
103
+ "Top Directory"
104
+ elsif @parent
105
+ "Parent Directory"
106
+ else
107
+ basename
108
+ end
109
+ end
91
110
  end
@@ -1,8 +1,8 @@
1
- %img#image
2
- %video#video{autoplay: true, controls: true}
3
- %audio#audio{autoplay: true, controls: true}
4
- #page-back.paginator ◀
5
- #page-next.paginator ▶
6
- #page-back-10.paginator ◀◀
7
- #page-next-10.paginator ▶▶
1
+ %img#image
2
+ %video#video{autoplay: true, controls: true}
3
+ %audio#audio{autoplay: true, controls: true}
4
+ #page-back.paginator ◀
5
+ #page-next.paginator ▶
6
+ #page-back-10.paginator ◀◀
7
+ #page-next-10.paginator ▶▶
8
8
  #page-toggle-size.paginator ⤡
@@ -1,21 +1,22 @@
1
- %h1 Listing of #{url_path}
2
- %table
3
- %thead
4
- %tr
5
- %th Name
6
- %th.new-tab
7
- %th.type Type
8
- %th.size Size
9
- %th.modified Modified
10
- %tbody
11
- - (directories + files).each do |file|
12
- - file = file.decorate
13
- %tr
14
- %td
15
- %span.icon= file.icon
16
- %a.default{href: file.href, **file.listing_attrs}= file.name
17
- %td
18
- %a.new-tab{href: file.href, target: "_blank", **file.listing_attrs} (New tab)
19
- %td= file.type
20
- %td= file.directory? ? "-" : number_to_human_size(file.size)
21
- %td= file.mtime.strftime("%e %b %Y %l:%M %p")
1
+ %h1 Listing of #{url_root}#{url_path}
2
+ .table-wrapper
3
+ %table
4
+ %thead
5
+ %tr
6
+ %th Name
7
+ %th.new-tab
8
+ %th.type Type
9
+ %th.size Size
10
+ %th.modified Modified
11
+ %tbody
12
+ - (directories + files).each do |file|
13
+ - file = decorate(file)
14
+ %tr
15
+ %td
16
+ %span.icon= file.icon
17
+ %a.default{href: file.href, **file.listing_attrs}= file.name
18
+ %td
19
+ %a.new-tab{href: file.href, target: "_blank", **file.listing_attrs} (New tab)
20
+ %td= file.type
21
+ %td= file.directory? || file.virtual? ? "-" : number_to_human_size(file.size)
22
+ %td= file.virtual? ? "-" : file.mtime.strftime("%e %b %Y %l:%M %p")
@@ -1,4 +1,5 @@
1
1
  * {
2
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
2
3
  box-sizing: border-box;
3
4
  }
4
5
 
@@ -13,4 +14,12 @@ body {
13
14
  -khtml-user-select: none;
14
15
  -ms-user-select: none;
15
16
  user-select: none;
17
+ }
18
+
19
+ a {
20
+ text-decoration: none;
21
+ }
22
+
23
+ a:hover, a:focus, a:active {
24
+ text-decoration: underline;
16
25
  }
@@ -1,5 +1,5 @@
1
1
  #gallery {
2
- padding: 0 20px;
2
+ padding: 0 100px;
3
3
  background: #333;
4
4
  transform: translateZ(0px);
5
5
  }
@@ -12,7 +12,6 @@
12
12
 
13
13
  #audio {
14
14
  width: 100%;
15
- padding: 0 100px;
16
15
  }
17
16
 
18
17
  @media (min-width: 992px) {
@@ -25,6 +24,10 @@
25
24
  display: block;
26
25
  }
27
26
 
27
+ #gallery.image {
28
+ padding: 0 20px;
29
+ }
30
+
28
31
  #gallery.image #image {
29
32
  display: block;
30
33
  }
@@ -0,0 +1,16 @@
1
+ #home {
2
+ padding: 20px;
3
+ }
4
+
5
+ #home h1 {
6
+ margin-top: 0;
7
+ }
8
+
9
+ #roots li {
10
+ font-size: 24px;
11
+ }
12
+
13
+ #roots a.new-tab {
14
+ display: inline-block;
15
+ margin-left: 20px;
16
+ }
@@ -0,0 +1,18 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %meta{charset: 'utf-8'}
5
+ %title Browse Listings
6
+ %meta{name: 'viewport', content: 'width=device-width, height=device-height, user-scalable=no'}
7
+ :css
8
+ #{include('normalize.css')}
9
+ #{include('common.css')}
10
+ #{include('home.css')}
11
+ %body
12
+ #home
13
+ %h1 Browse Listings
14
+ %ul#roots
15
+ - roots.each do |root|
16
+ %li
17
+ %a{href: root}= root
18
+ %a.new-tab{href: root, target: "_blank"} (New Tab)
@@ -12,25 +12,4 @@ body.split-screen #listing {
12
12
  body.split-screen #gallery {
13
13
  width: 50%;
14
14
  float: right;
15
- }
16
- /*
17
- @media screen and (min-width: 1800px) {
18
- #listing {
19
- float: left;
20
- width: 800px;
21
- height: 100%;
22
- overflow: auto;
23
- }
24
-
25
- #gallery {
26
- margin-left: 800px;
27
- }
28
-
29
- body.gallery-expanded #listing {
30
- display: none;
31
- }
32
-
33
- body.gallery-expanded #gallery {
34
- margin-left: 0;
35
- }
36
- }*/
15
+ }
@@ -1,19 +1,19 @@
1
- !!!
2
- %html
3
- %head
4
- %meta{charset: 'utf-8'}
5
- %title Listing of #{url_path}
6
- %meta{name: 'viewport', content: 'width=device-width, height=device-height, user-scalable=no'}
7
- :css
8
- #{include('normalize.css')}
9
- #{include('common.css')}
10
- #{include('index.css')}
11
- #{include('listing.css')}
12
- #{include('gallery.css')}
13
-
14
- :javascript
15
- #{include('gallery.js')}
16
- %body
17
- - if files.any? { |f| f.decorate.media? }
18
- #gallery!= partial('gallery')
19
- #listing!= partial('listing', { url_path: url_path, directories: directories, files: files })
1
+ !!!
2
+ %html
3
+ %head
4
+ %meta{charset: 'utf-8'}
5
+ %title Listing of #{url_root}#{url_path}
6
+ %meta{name: 'viewport', content: 'width=device-width, height=device-height, user-scalable=no'}
7
+ :css
8
+ #{include('normalize.css')}
9
+ #{include('common.css')}
10
+ #{include('index.css')}
11
+ #{include('listing.css')}
12
+ #{include('gallery.css')}
13
+
14
+ :javascript
15
+ #{include('gallery.js')}
16
+ %body
17
+ - if files.any? { |f| decorate(f).media? }
18
+ #gallery!= partial('gallery')
19
+ #listing!= partial('listing', { url_root: url_root, url_path: url_path, directories: directories, files: files })
@@ -1,43 +1,55 @@
1
- #listing {
2
- padding: 20px;
3
- }
4
-
5
- #listing h1 {
6
- margin-top: 0;
7
- }
8
-
9
- table {
10
- width: 100%;
11
- border-collapse: collapse;
12
- border: 1px solid #ddd;
13
- table-layout: fixed;
14
- }
15
-
16
- th, td {
17
- padding: 5px;
18
- border: 1px solid #ddd;
19
- }
20
-
21
- tbody > tr:nth-of-type(odd) {
22
- background-color: #f9f9f9;
23
- }
24
-
25
- th.new-tab {
26
- width: 6em;
27
- }
28
-
29
- th.type {
30
- width: 4em;
31
- }
32
-
33
- th.size {
34
- width: 6em;
35
- }
36
-
37
- th.modified {
38
- width: 12em;
39
- }
40
-
41
- span.icon {
42
- margin-right: 0.2em;
43
- }
1
+ #listing {
2
+ padding: 20px;
3
+ }
4
+
5
+ #listing h1 {
6
+ margin-top: 0;
7
+ }
8
+
9
+ .table-wrapper {
10
+ overflow: auto;
11
+ }
12
+
13
+ table {
14
+ width: 100%;
15
+ min-width: 800px;
16
+ border-collapse: collapse;
17
+ border: 1px solid #ddd;
18
+ table-layout: fixed;
19
+ }
20
+
21
+ th, td {
22
+ padding: 5px;
23
+ border: 1px solid #ddd;
24
+ }
25
+
26
+ tbody > tr:nth-of-type(odd) {
27
+ background-color: #f9f9f9;
28
+ }
29
+
30
+ th.new-tab {
31
+ width: 6em;
32
+ }
33
+
34
+ th.type {
35
+ width: 4em;
36
+ }
37
+
38
+ th.size {
39
+ width: 6em;
40
+ }
41
+
42
+ th.modified {
43
+ width: 12em;
44
+ }
45
+
46
+ span.icon {
47
+ display: inline-block;
48
+ margin-right: 0.2em;
49
+ width: 1.4em;
50
+ text-align: center;
51
+ }
52
+
53
+ a.default.top, a.default.parent {
54
+ font-style: oblique;
55
+ }
@@ -1,3 +1,3 @@
1
1
  module Servel
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: servel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brenton "B-Train" Fletcher
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-04 00:00:00.000000000 Z
11
+ date: 2018-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -125,17 +125,19 @@ files:
125
125
  - bin/servel
126
126
  - bin/setup
127
127
  - lib/servel.rb
128
- - lib/servel/core_ext/pathname.rb
128
+ - lib/servel/app.rb
129
+ - lib/servel/cli.rb
129
130
  - lib/servel/haml_context.rb
131
+ - lib/servel/home_app.rb
130
132
  - lib/servel/locals.rb
131
- - lib/servel/middleware.rb
132
133
  - lib/servel/pathname_decorator.rb
133
- - lib/servel/servel.rb
134
134
  - lib/servel/templates/_gallery.haml
135
135
  - lib/servel/templates/_listing.haml
136
136
  - lib/servel/templates/common.css
137
137
  - lib/servel/templates/gallery.css
138
138
  - lib/servel/templates/gallery.js
139
+ - lib/servel/templates/home.css
140
+ - lib/servel/templates/home.haml
139
141
  - lib/servel/templates/index.css
140
142
  - lib/servel/templates/index.haml
141
143
  - lib/servel/templates/listing.css
@@ -1,5 +0,0 @@
1
- class Pathname
2
- def decorate(parent = false)
3
- Servel::PathnameDecorator.new(self, parent)
4
- end
5
- end
@@ -1,36 +0,0 @@
1
- class Servel::Middleware
2
- def initialize(app, options = {})
3
- @app = app
4
- @root = Pathname.new(options[:root])
5
- @file_server = Rack::File.new(@root.to_s)
6
- end
7
-
8
- def call(env)
9
- path = env["PATH_INFO"]
10
- url_path = url_path_for(path)
11
- fs_path = @root + url_path[1..-1]
12
-
13
- return @file_server.call(env) unless fs_path.directory?
14
-
15
- url_path << "/" unless url_path.end_with?("/")
16
-
17
- return [302, { "Location" => url_path }, []] unless path == "" || path.end_with?("/")
18
-
19
- index(url_path, fs_path)
20
- end
21
-
22
- def url_path_for(url_path)
23
- url_path = Rack::Utils.unescape_path(url_path)
24
- raise unless Rack::Utils.valid_path?(url_path)
25
-
26
- Rack::Utils.clean_path_info(url_path)
27
- end
28
-
29
- def index(url_path, fs_path)
30
- @haml_context ||= Servel::HamlContext.new
31
- locals = Servel::Locals.new(url_path: url_path, fs_path: fs_path, root: @root).locals
32
- body = @haml_context.render('index.haml', locals)
33
-
34
- [200, {}, [body]]
35
- end
36
- end
@@ -1,21 +0,0 @@
1
- class Servel::Servel
2
- def initialize(root)
3
- @root = root
4
- end
5
-
6
- def start
7
- Rack::Handler::Puma.run(build_app)
8
- end
9
-
10
- def build_app
11
- root = @root
12
-
13
- Rack::Builder.new do
14
- use(Servel::Middleware, root: root)
15
-
16
- run ->(env) do
17
- [404, {}, []]
18
- end
19
- end
20
- end
21
- end