servel 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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