yetty 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -0
- data/LICENSE +0 -0
- data/README.md +66 -0
- data/bin/yetty +9 -4
- data/lib/yetty.rb +15 -0
- data/lib/yetty/command.rb +136 -0
- data/lib/yetty/play.rb +23 -0
- data/lib/yetty/push.rb +44 -0
- data/lib/yetty/record.rb +73 -0
- data/lib/yetty/site.rb +23 -0
- data/lib/yetty/site/app.rb +91 -0
- data/lib/yetty/site/static/assets/term/bg-gradient.png +0 -0
- data/lib/yetty/site/static/config.rb +26 -0
- data/lib/yetty/site/static/css/fd-slider.css +1004 -0
- data/lib/yetty/site/static/css/fonts/bootstrap/glyphicons-halflings-regular.eot +0 -0
- data/lib/yetty/site/static/css/fonts/bootstrap/glyphicons-halflings-regular.svg +229 -0
- data/lib/yetty/site/static/css/fonts/bootstrap/glyphicons-halflings-regular.ttf +0 -0
- data/lib/yetty/site/static/css/fonts/bootstrap/glyphicons-halflings-regular.woff +0 -0
- data/lib/yetty/site/static/css/player.css +165 -0
- data/lib/yetty/site/static/css/styles.css +8035 -0
- data/lib/yetty/site/static/js/bootstrap.js +2323 -0
- data/lib/yetty/site/static/js/bootstrap.min.js +12 -0
- data/lib/yetty/site/static/js/config.json +429 -0
- data/lib/yetty/site/static/js/jquery-2.1.1.min.js +4 -0
- data/lib/yetty/site/static/js/nyan/runner.js +92 -0
- data/lib/yetty/site/static/js/nyan/three.min.js +724 -0
- data/lib/yetty/site/static/js/nyan/threex.nyancat.js +193 -0
- data/lib/yetty/site/static/js/nyan/threex.nyancatrainbow.js +89 -0
- data/lib/yetty/site/static/js/nyan/threex.nyancatsound.js +33 -0
- data/lib/yetty/site/static/js/nyan/threex.nyancatstars.js +116 -0
- data/lib/yetty/site/static/js/player/fd-slider.min.js +2 -0
- data/lib/yetty/site/static/js/player/player.js +371 -0
- data/lib/yetty/site/static/js/player/term.js +4177 -0
- data/lib/yetty/site/static/sass/_bootstrap-variables.scss +818 -0
- data/lib/yetty/site/static/sass/player.scss +139 -0
- data/lib/yetty/site/static/sass/styles.scss +3 -0
- data/lib/yetty/site/static/sounds/nyanlooped.mp3 +0 -0
- data/lib/yetty/site/static/sounds/nyanslow.mp3 +0 -0
- data/lib/yetty/site/views/about.haml +8 -0
- data/lib/yetty/site/views/index.haml +13 -0
- data/lib/yetty/site/views/layout.haml +33 -0
- data/lib/yetty/site/views/recording.haml +34 -0
- data/lib/yetty/site/views/recordings.haml +7 -0
- data/lib/yetty/site/views/user.haml +28 -0
- data/lib/yetty/site/views/users.haml +16 -0
- data/lib/yetty/ui.rb +103 -0
- data/lib/yetty/user.rb +42 -0
- data/lib/yetty/version.rb +4 -0
- data/yetty.gemspec +21 -0
- metadata +49 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f45bd672b89d1030a4d3dc6c661c9509feb07d0
|
4
|
+
data.tar.gz: 8e357c6886ad138f1c56cd727ac340d62fcad281
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a98db022addf0ad2ff0ad3fc2864454e9ad365b666620331cd73b665b23358a8aad67855a907fe2c132434392f1992540d0a1a1511fee92158cd3a1ab9cc97b6
|
7
|
+
data.tar.gz: 21bad94340f8d3769204c587cdc4f0678aac9c87160f1b6aed8af17d7cbc24784fcd9f755dc8d6be4b2c9bea6caba2f5605d1ac0d889171112f86266c829bae8
|
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
File without changes
|
data/README.md
CHANGED
@@ -0,0 +1,66 @@
|
|
1
|
+
## Yetty
|
2
|
+
|
3
|
+
With shelr.tv[1] now defunct, I wanted an easy way to spin out
|
4
|
+
my own to easily share terminal recordings. Yetty is a wrapper
|
5
|
+
around shelr[2] that removes some of the extra features it
|
6
|
+
provids. It also has a very simple web UI builtin that hooks into
|
7
|
+
an object store to persist dump files.
|
8
|
+
|
9
|
+
### Is it secure?
|
10
|
+
|
11
|
+
No, not at all. This is configuration based and user uploads are
|
12
|
+
simply prefixed within the bucket. But this is just a friendly
|
13
|
+
tool, so if bad things happen, maybe your friends aren't so friendly.
|
14
|
+
|
15
|
+
### Web UI?
|
16
|
+
|
17
|
+
Yep. It's a very simple sinatra application. It pulls information
|
18
|
+
directly from the object store. So not built for speed or scale.
|
19
|
+
|
20
|
+
### Private?
|
21
|
+
|
22
|
+
Kinda. Private pushes are simply not listed publicly. So you'll need
|
23
|
+
the direct link to access them (much like private gists).
|
24
|
+
|
25
|
+
### Configuration
|
26
|
+
|
27
|
+
Configuration is a JSON file. Locations checked in order of precedence:
|
28
|
+
|
29
|
+
* ./yetty
|
30
|
+
* ~/.yetty
|
31
|
+
* /etc/yetty/config.json
|
32
|
+
|
33
|
+
File contents:
|
34
|
+
|
35
|
+
```json
|
36
|
+
{
|
37
|
+
"site": {
|
38
|
+
"url": "http://localhost:4000"
|
39
|
+
},
|
40
|
+
"user": {
|
41
|
+
"username": "YOUR_USERNAME",
|
42
|
+
"storage": {
|
43
|
+
"provider": "aws",
|
44
|
+
"bucket": "my-yetty-bucket",
|
45
|
+
"credentials": {
|
46
|
+
"aws_access_key_id": "...",
|
47
|
+
"aws_secret_access_key": "...",
|
48
|
+
"aws_bucket_region": "..."
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
```
|
54
|
+
|
55
|
+
The `credentials` section are the credentials expected
|
56
|
+
by miasma[3] based on the provider in use.
|
57
|
+
|
58
|
+
### Thanks!
|
59
|
+
|
60
|
+
A big thanks to @antono for the shelr[2] library. Also
|
61
|
+
to @jeromeetienne for the beautiful nyancat[4]
|
62
|
+
|
63
|
+
[1]: https://github.com/antono/shelr.tv "record you shell and publish it"
|
64
|
+
[2]: https://github.com/antono/shelr "Console screencasting tool"
|
65
|
+
[3]: https://github.com/chrisroberts/miasma "Cloud modeling library"
|
66
|
+
[4]: https://github.com/jeromeetienne/threex.nyancat "three.js extension to make nyancat"
|
data/bin/yetty
CHANGED
@@ -23,7 +23,6 @@ begin
|
|
23
23
|
description 'Push JSON recording'
|
24
24
|
on :c, :config=, 'JSON configuration file'
|
25
25
|
on :p, :private, 'Not publicly listed'
|
26
|
-
on :o, :overwrite, 'Overwrite if already exists'
|
27
26
|
run do |opts, args|
|
28
27
|
Yetty::Push.new({:push => opts.to_hash}, args).execute!
|
29
28
|
end
|
@@ -37,11 +36,17 @@ begin
|
|
37
36
|
end
|
38
37
|
end
|
39
38
|
|
39
|
+
command 'play' do
|
40
|
+
description 'Play terminal'
|
41
|
+
run do |opts, args|
|
42
|
+
Yetty::Play.new({:play => opts}, args).execute!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
40
46
|
command 'user' do
|
41
|
-
description '
|
42
|
-
on :c, :config=, 'JSON configuration file'
|
47
|
+
description 'Set user information'
|
43
48
|
run do |opts, args|
|
44
|
-
Yetty::
|
49
|
+
Yetty::User.new({:user => opts.to_hash}, args).execute!
|
45
50
|
end
|
46
51
|
end
|
47
52
|
|
data/lib/yetty.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'yetty/version'
|
2
|
+
|
3
|
+
# TTY utility
|
4
|
+
module Yetty
|
5
|
+
autoload :Command, 'yetty/command'
|
6
|
+
autoload :Play, 'yetty/play'
|
7
|
+
autoload :Push, 'yetty/push'
|
8
|
+
autoload :Record, 'yetty/record'
|
9
|
+
autoload :Site, 'yetty/site'
|
10
|
+
autoload :Ui, 'yetty/ui'
|
11
|
+
autoload :User, 'yetty/user'
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'miasma'
|
15
|
+
require 'miasma/utils/smash'
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'yetty'
|
2
|
+
|
3
|
+
module Yetty
|
4
|
+
# Abstract command class
|
5
|
+
class Command
|
6
|
+
|
7
|
+
# @return [Hash] options
|
8
|
+
attr_reader :options
|
9
|
+
# @return [Array] cli arguments
|
10
|
+
attr_reader :arguments
|
11
|
+
# @return [Ui]
|
12
|
+
attr_reader :ui
|
13
|
+
|
14
|
+
# Default configuration file locations
|
15
|
+
DEFAULT_CONFIGURATION_FILES = [
|
16
|
+
'./.yetty',
|
17
|
+
'~/.yetty',
|
18
|
+
'/etc/yetty/config.json'
|
19
|
+
]
|
20
|
+
|
21
|
+
# Build new command instance
|
22
|
+
#
|
23
|
+
# @return [self]
|
24
|
+
def initialize(opts, args)
|
25
|
+
@options = opts.to_smash
|
26
|
+
@arguments = args
|
27
|
+
@ui = Ui.new
|
28
|
+
load_config!
|
29
|
+
end
|
30
|
+
|
31
|
+
# Execute the command
|
32
|
+
#
|
33
|
+
# @return [TrueClass]
|
34
|
+
def execute!
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
# Command specific options
|
41
|
+
#
|
42
|
+
# @return [Hash]
|
43
|
+
def opts
|
44
|
+
options.fetch(self.class.name.split('::').last.downcase, {})
|
45
|
+
end
|
46
|
+
|
47
|
+
# User specific options
|
48
|
+
#
|
49
|
+
# @return [Smash]
|
50
|
+
def user
|
51
|
+
result = options[:user]
|
52
|
+
unless(result)
|
53
|
+
raise 'No user information defined!'
|
54
|
+
else
|
55
|
+
result = result.to_smash
|
56
|
+
unless(result[:username])
|
57
|
+
result[:username] = ENV['USER']
|
58
|
+
end
|
59
|
+
result
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Miasma::Models::Storage]
|
64
|
+
def storage
|
65
|
+
if(user[:storage])
|
66
|
+
Miasma.api(
|
67
|
+
:type => :storage,
|
68
|
+
:provider => user[:storage][:provider],
|
69
|
+
:credentials => user[:storage][:credentials]
|
70
|
+
)
|
71
|
+
else
|
72
|
+
raise 'No remote storage configuration found!'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Miasma::Models::Storage::Bucket]
|
77
|
+
def bucket
|
78
|
+
unless(user.get(:storage, :bucket))
|
79
|
+
raise 'No bucket defined within storage configuration!'
|
80
|
+
end
|
81
|
+
bucket = storage.buckets.get(user[:storage][:bucket])
|
82
|
+
unless(bucket)
|
83
|
+
bucket = storage.buckets.build
|
84
|
+
bucket.name = user[:storage][:bucket]
|
85
|
+
bucket.save
|
86
|
+
bucket = storage.buckets.reload.get(user[:storage][:bucket])
|
87
|
+
end
|
88
|
+
bucket
|
89
|
+
end
|
90
|
+
|
91
|
+
# Load configuration file and merge opts
|
92
|
+
# on top of file values
|
93
|
+
#
|
94
|
+
# @return [Hash]
|
95
|
+
def load_config!
|
96
|
+
if(options[:config])
|
97
|
+
content = File.read(options[:config])
|
98
|
+
else
|
99
|
+
path = DEFAULT_CONFIGURATION_FILES.detect do |check|
|
100
|
+
full_check = File.expand_path(check)
|
101
|
+
File.exists?(full_check)
|
102
|
+
end
|
103
|
+
content = File.read(path) if path
|
104
|
+
end
|
105
|
+
if(content)
|
106
|
+
@options = MultiJson.load(content).to_smash.deep_merge(options)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Wrap action within nice text. Output resulting Hash if provided
|
111
|
+
#
|
112
|
+
# @param msg [String] message of action in progress
|
113
|
+
# @yieldblock action to execute
|
114
|
+
# @yieldreturn [Hash] result to output
|
115
|
+
# @return [TrueClass]
|
116
|
+
def run_action(msg)
|
117
|
+
ui.info("#{msg}... ", :nonewline)
|
118
|
+
begin
|
119
|
+
result = yield
|
120
|
+
ui.puts ui.color('complete!', :green, :bold)
|
121
|
+
if(result.is_a?(Hash))
|
122
|
+
ui.puts '---> Results:'
|
123
|
+
result.each do |k,v|
|
124
|
+
ui.puts " #{ui.color("#{k}:", :bold)} #{v}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
rescue => e
|
128
|
+
ui.puts ui.color('error!', :red, :bold)
|
129
|
+
ui.error "Reason - #{e}"
|
130
|
+
raise
|
131
|
+
end
|
132
|
+
true
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
data/lib/yetty/play.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'yetty'
|
2
|
+
require 'shelr'
|
3
|
+
|
4
|
+
module Yetty
|
5
|
+
class Player < Shelr::Player
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Yetty
|
10
|
+
# Play command class
|
11
|
+
class Play < Command
|
12
|
+
|
13
|
+
# Invoke shelr record
|
14
|
+
def execute!
|
15
|
+
if(arguments.first.start_with?('http'))
|
16
|
+
Yetty::Player.play_remote(arguments.first)
|
17
|
+
else
|
18
|
+
Yetty::Player.play_dump(arguments.first)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
data/lib/yetty/push.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'yetty'
|
2
|
+
|
3
|
+
module Yetty
|
4
|
+
# Push command class
|
5
|
+
class Push < Command
|
6
|
+
|
7
|
+
# Push shelr json to storage
|
8
|
+
def execute!
|
9
|
+
run_action('Updating user list') do
|
10
|
+
file = bucket.files.get('userlist.json')
|
11
|
+
content = file ? MultiJson.load(file.body.readpartial).to_smash : Smash.new(:users => [])
|
12
|
+
unless(content[:users].include?(user[:username]))
|
13
|
+
content[:users].push(user[:username]).uniq!
|
14
|
+
file = bucket.files.build
|
15
|
+
file.name = 'userlist.json'
|
16
|
+
file.content_type = 'application/json'
|
17
|
+
file.body = MultiJson.dump(content)
|
18
|
+
file.save
|
19
|
+
end
|
20
|
+
end
|
21
|
+
filename = arguments.first
|
22
|
+
run_action("Pushing file #{filename}") do
|
23
|
+
key_name = File.basename(filename).sub(/\.[^\.]+$/, '')
|
24
|
+
file_content = File.read(filename)
|
25
|
+
data = MultiJson.load(file_content).to_smash
|
26
|
+
state = data[:private] || options[:push][:private] ? 'private' : 'public'
|
27
|
+
key_name = File.join(user[:username], state, "#{Time.now.to_i}-#{key_name}.json")
|
28
|
+
file = bucket.files.build
|
29
|
+
file.name = key_name
|
30
|
+
file.content_type = 'application/json'
|
31
|
+
file.body = file_content
|
32
|
+
file.save
|
33
|
+
encoded_key = Base64.urlsafe_encode64(file.name)
|
34
|
+
Smash.new(
|
35
|
+
:bucket => bucket.name,
|
36
|
+
:key => file.name,
|
37
|
+
:encoded_key => encoded_key,
|
38
|
+
:link => options.get(:site, :url) ? File.join(options[:site][:url], 'recording', encoded_key) : 'Not configured!'
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
data/lib/yetty/record.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'yetty'
|
2
|
+
require 'shelr'
|
3
|
+
|
4
|
+
module Yetty
|
5
|
+
|
6
|
+
class Publisher < Shelr::Publisher
|
7
|
+
|
8
|
+
attr_reader :file_name
|
9
|
+
|
10
|
+
def initialize(file_name='yetty.json')
|
11
|
+
@file_name = file_name
|
12
|
+
super()
|
13
|
+
end
|
14
|
+
|
15
|
+
def prepare(id)
|
16
|
+
out = {}
|
17
|
+
['meta', 'timing', 'typescript'].each do |file|
|
18
|
+
out[file] = File.read(File.join(Shelr.data_dir(id), file))
|
19
|
+
end
|
20
|
+
|
21
|
+
meta = JSON.parse(out.delete('meta'))
|
22
|
+
meta.each { |k,v| out[k] = v }
|
23
|
+
STDOUT.print 'Description: '
|
24
|
+
out['description'] = STDIN.gets.strip
|
25
|
+
STDOUT.print 'Tags (ex: howto, linux): '
|
26
|
+
out['tags'] = STDIN.gets.strip
|
27
|
+
out['private'] = @private
|
28
|
+
return out.to_json
|
29
|
+
end
|
30
|
+
|
31
|
+
def dump_filename
|
32
|
+
file_name
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class Recorder < Shelr::Recorder
|
38
|
+
|
39
|
+
def record!(options={})
|
40
|
+
ensure_terminal_has_good_size
|
41
|
+
check_record_dir
|
42
|
+
with_lock_file do
|
43
|
+
init_terminal
|
44
|
+
request_metadata
|
45
|
+
Shelr.terminal.puts_line
|
46
|
+
STDOUT.puts "=> Your session started"
|
47
|
+
STDOUT.puts "=> Please, do not resize your terminal while recording"
|
48
|
+
STDOUT.puts "=> Press Ctrl+D or 'exit' to finish recording"
|
49
|
+
Shelr.terminal.puts_line
|
50
|
+
start_sound_recording if options[:sound]
|
51
|
+
system(recorder_cmd)
|
52
|
+
stop_sound_recording if options[:sound]
|
53
|
+
save_as_typescript if Shelr.backend == 'ttyrec'
|
54
|
+
Shelr.terminal.puts_line
|
55
|
+
STDOUT.puts "=> Session finished"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
module Yetty
|
63
|
+
# Record command class
|
64
|
+
class Record < Command
|
65
|
+
|
66
|
+
# Invoke shelr record
|
67
|
+
def execute!
|
68
|
+
Yetty::Recorder.record!(opts)
|
69
|
+
Yetty::Publisher.new(arguments.first || 'yetty.json').dump('last')
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
data/lib/yetty/site.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'yetty'
|
2
|
+
|
3
|
+
module Yetty
|
4
|
+
class Site < Yetty::Command
|
5
|
+
|
6
|
+
autoload :App, 'yetty/site/app'
|
7
|
+
|
8
|
+
# Start the web UI
|
9
|
+
#
|
10
|
+
# @return true
|
11
|
+
def execute!
|
12
|
+
if(opts[:port])
|
13
|
+
App.port = opts[:port]
|
14
|
+
end
|
15
|
+
if(opts[:bind])
|
16
|
+
App.bind = opts[:bind]
|
17
|
+
end
|
18
|
+
App.server = options.fetch(:server, 'webrick')
|
19
|
+
App.run!
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'sinatra/base'
|
3
|
+
|
4
|
+
module Yetty
|
5
|
+
class Site
|
6
|
+
# Web UI
|
7
|
+
class App < Sinatra::Base
|
8
|
+
|
9
|
+
def bucket
|
10
|
+
storage = Miasma.api(
|
11
|
+
:type => :storage,
|
12
|
+
:provider => :aws,
|
13
|
+
:credentials => {
|
14
|
+
:aws_access_key_id => ENV['MIASMA_AWS_ACCESS_KEY_ID'],
|
15
|
+
:aws_secret_access_key => ENV['MIASMA_AWS_SECRET_ACCESS_KEY'],
|
16
|
+
:aws_region => ENV['MIASMA_AWS_REGION'],
|
17
|
+
:aws_bucket_region => 'us-west-2'
|
18
|
+
}
|
19
|
+
)
|
20
|
+
bucket = storage.buckets.get('cr-test-00')
|
21
|
+
end
|
22
|
+
|
23
|
+
set :public_folder, File.join(File.dirname(__FILE__), 'static')
|
24
|
+
|
25
|
+
get '/' do
|
26
|
+
haml :index
|
27
|
+
end
|
28
|
+
|
29
|
+
get '/recordings' do
|
30
|
+
haml :recordings
|
31
|
+
end
|
32
|
+
|
33
|
+
get '/recording/:info' do
|
34
|
+
rec_path = Base64.urlsafe_decode64(params[:info])
|
35
|
+
user, state, name = rec_path.split('/')
|
36
|
+
rec_url = bucket.files.get(rec_path).url
|
37
|
+
haml(
|
38
|
+
:recording,
|
39
|
+
:locals => {
|
40
|
+
:user => user,
|
41
|
+
:state => state,
|
42
|
+
:name => name,
|
43
|
+
:url => rec_url
|
44
|
+
}
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
get '/users' do
|
49
|
+
file = bucket.files.get('userlist.json')
|
50
|
+
users = file ? MultiJson.load(file.body.readpartial).to_smash[:users] : []
|
51
|
+
haml(
|
52
|
+
:users,
|
53
|
+
:locals => {
|
54
|
+
:users => users
|
55
|
+
}
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
get '/users/:username' do
|
60
|
+
user_file = bucket.files.get(
|
61
|
+
File.join(params[:username], 'userinfo.json')
|
62
|
+
)
|
63
|
+
if(user_file)
|
64
|
+
user_info = MultiJson.load(user_file.read).to_smash
|
65
|
+
else
|
66
|
+
user_info = Smash.new
|
67
|
+
end
|
68
|
+
prefix = [params[:username], 'public'].join('/')
|
69
|
+
records = bucket.files.filter(
|
70
|
+
:prefix => [
|
71
|
+
params[:username],
|
72
|
+
'public'
|
73
|
+
].join('/')
|
74
|
+
)
|
75
|
+
haml(
|
76
|
+
:user,
|
77
|
+
:locals => {
|
78
|
+
:username => params[:username],
|
79
|
+
:user_info => user_info,
|
80
|
+
:records => records
|
81
|
+
}
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
get '/about' do
|
86
|
+
haml :about
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|