stastic 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +11 -0
- data/bin/stastic +11 -1
- data/lib/server.rb +43 -0
- data/lib/stastic/client.rb +72 -0
- data/lib/stastic/command.rb +41 -0
- data/lib/stastic/commands/auth.rb +8 -0
- data/lib/stastic/commands/base.rb +96 -0
- data/lib/stastic/commands/create.rb +68 -0
- data/lib/stastic/commands/domains.rb +41 -0
- data/lib/stastic/commands/help.rb +33 -0
- data/lib/stastic/commands/info.rb +26 -0
- data/lib/stastic/commands/list.rb +37 -0
- data/lib/stastic/commands/preview.rb +9 -0
- data/lib/stastic/commands/publish.rb +34 -0
- data/lib/stastic/commands/rename.rb +25 -0
- data/lib/stastic/commands/set.rb +54 -0
- data/lib/stastic/config.rb +79 -0
- data/lib/stastic/generator.rb +50 -0
- data/lib/stastic/generators/default.rb +31 -0
- data/lib/stastic/generators/jekyll.rb +72 -0
- data/lib/stastic.rb +1 -1
- data/lib/trollop.rb +782 -0
- data/templates/default/index.html +7 -0
- metadata +73 -6
data/Rakefile
ADDED
data/bin/stastic
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
3
6
|
require 'stastic'
|
7
|
+
require 'stastic/command'
|
8
|
+
require 'stastic/generator'
|
9
|
+
|
10
|
+
args = ARGV.dup
|
11
|
+
ARGV.clear
|
12
|
+
command = args.shift.strip rescue 'help'
|
13
|
+
|
14
|
+
Stastic::Command.run(command, args)
|
4
15
|
|
5
|
-
puts "stastic (http://stastic.com) - #{Stastic::VERSION}"
|
data/lib/server.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'webrick'
|
2
|
+
|
3
|
+
class Server < WEBrick::HTTPServer
|
4
|
+
class NonCachingFileHandler < WEBrick::HTTPServlet::FileHandler
|
5
|
+
def prevent_caching(res)
|
6
|
+
res['ETag'] = nil
|
7
|
+
res['Last-Modified'] = Time.now + 100**4
|
8
|
+
res['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
|
9
|
+
res['Pragma'] = 'no-cache'
|
10
|
+
res['Expires'] = Time.now - 100**4
|
11
|
+
end
|
12
|
+
|
13
|
+
def do_GET(req, res)
|
14
|
+
super
|
15
|
+
prevent_caching(res)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(path, port)
|
20
|
+
@port = port
|
21
|
+
@path = path
|
22
|
+
mime_types = WEBrick::HTTPUtils::DefaultMimeTypes
|
23
|
+
mime_types.store 'js', 'application/javascript'
|
24
|
+
super(
|
25
|
+
:Port => @port,
|
26
|
+
:MimeTypes => mime_types
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def start
|
31
|
+
mount("/", NonCachingFileHandler, @path)
|
32
|
+
t = Thread.new {
|
33
|
+
super
|
34
|
+
}
|
35
|
+
|
36
|
+
trap("INT") { shutdown }
|
37
|
+
t.join()
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Stastic
|
5
|
+
class InvalidCredentials < RestClient::Forbidden; end
|
6
|
+
class ValidationFailed < RestClient::UnprocessableEntity; end
|
7
|
+
|
8
|
+
class Client
|
9
|
+
attr_accessor :user, :token, :host
|
10
|
+
|
11
|
+
def self.authenticate(email, password)
|
12
|
+
client = new
|
13
|
+
client.request(:get, "/authenticate", {:email => email, :password => password})["token"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.request(method, uri, payload = nil, headers = {})
|
17
|
+
client = new
|
18
|
+
client.request(method, uri, payload, headers)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.create(name = nil)
|
22
|
+
if name
|
23
|
+
request(:post, "/sites", {:site => {:name => name}})
|
24
|
+
else
|
25
|
+
request(:post, "/sites")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.list
|
30
|
+
request(:get, "/sites")
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.publish(site_id, archive_path)
|
34
|
+
request(:put, "/sites/#{site_id}/publish", {:site => {:archive => File.new(archive_path)}})
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.rename(site_id, name)
|
38
|
+
request(:put, "/sites/#{site_id}", {:site => {:name => name}})
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.add_domain(site_id, domain_name)
|
42
|
+
request(:post, "/sites/#{site_id}/domains", {:domain => {:host => domain_name}})
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.remove_domain(site_id, domain_name)
|
46
|
+
request(:delete, "/sites/#{site_id}/domains/#{domain_name}")
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize(host = "http://stastic.com")
|
50
|
+
if Stastic::Config.exists?
|
51
|
+
self.user = Stastic::Config.user
|
52
|
+
self.token = Stastic::Config.token
|
53
|
+
end
|
54
|
+
self.host = host
|
55
|
+
end
|
56
|
+
|
57
|
+
def request(method, uri, payload = nil, headers = {})
|
58
|
+
headers.merge!(:accept => 'json', "X-STASTIC" => Stastic::VERSION)
|
59
|
+
|
60
|
+
response = RestClient::Request.execute(
|
61
|
+
:method => method,
|
62
|
+
:url => host + uri,
|
63
|
+
:headers => headers,
|
64
|
+
:payload => payload,
|
65
|
+
:user => user,
|
66
|
+
:password => token)
|
67
|
+
|
68
|
+
return JSON::parse(response.body)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'trollop'
|
2
|
+
require 'stastic/config'
|
3
|
+
require 'stastic/client'
|
4
|
+
require 'stastic/commands/base'
|
5
|
+
Dir["#{File.dirname(__FILE__)}/commands/*.rb"].each { |c| require c }
|
6
|
+
|
7
|
+
module Stastic
|
8
|
+
module Command
|
9
|
+
extend self
|
10
|
+
|
11
|
+
class UnknownCommand < RuntimeError; end
|
12
|
+
class InvalidOptions < RuntimeError; end
|
13
|
+
|
14
|
+
|
15
|
+
def run(cmd, args)
|
16
|
+
begin
|
17
|
+
klass, method = parse_command(cmd)
|
18
|
+
runner = klass.new(args)
|
19
|
+
raise UnknownCommand unless runner.respond_to?(method)
|
20
|
+
runner.send(method)
|
21
|
+
rescue UnknownCommand
|
22
|
+
puts "Invalid command `#{cmd}`. Run `stastic help` for available commands."
|
23
|
+
rescue InvalidOptions
|
24
|
+
puts "Invalid options for `#{cmd}`. Run `stastic help` for more information."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def parse_command(cmd)
|
31
|
+
begin
|
32
|
+
klass, method = cmd.split(':')
|
33
|
+
return eval("Stastic::Command::#{klass.capitalize}"), method || :index
|
34
|
+
rescue NameError, NoMethodError
|
35
|
+
puts $!.inspect
|
36
|
+
raise UnknownCommand
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Stastic::Command
|
2
|
+
class Base
|
3
|
+
|
4
|
+
def initialize(args); end
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def request_credentials
|
9
|
+
print "Enter your Stastic login credentials.\n\n"
|
10
|
+
attempts = 0
|
11
|
+
|
12
|
+
begin
|
13
|
+
attempts += 1
|
14
|
+
print " Email: "
|
15
|
+
email = ask
|
16
|
+
print " Password: "
|
17
|
+
password = ask_for_password
|
18
|
+
token = Stastic::Client.authenticate(email, password)
|
19
|
+
rescue Stastic::InvalidCredentials
|
20
|
+
if attempts < 3
|
21
|
+
print "\nInvalid credentials - try again.\n\n"
|
22
|
+
retry
|
23
|
+
else
|
24
|
+
print "\nInvalid credentials -- too many tries!\n"
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Stastic::Config.update({:user => email, :token => token}, :global)
|
30
|
+
print "\nYour credentials have been saved.\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
def echo_off
|
34
|
+
system "stty -echo"
|
35
|
+
end
|
36
|
+
|
37
|
+
def echo_on
|
38
|
+
system "stty echo"
|
39
|
+
end
|
40
|
+
|
41
|
+
def ask
|
42
|
+
gets.strip
|
43
|
+
end
|
44
|
+
|
45
|
+
def ask_for_password
|
46
|
+
echo_off
|
47
|
+
password = ask
|
48
|
+
puts
|
49
|
+
echo_on
|
50
|
+
return password
|
51
|
+
end
|
52
|
+
|
53
|
+
def with_valid_user
|
54
|
+
request_credentials unless Stastic::Config.credentials?
|
55
|
+
begin
|
56
|
+
yield
|
57
|
+
rescue RestClient::Forbidden
|
58
|
+
request_credentials
|
59
|
+
retry
|
60
|
+
rescue RestClient::UnprocessableEntity => e
|
61
|
+
puts "There was an error with the request:"
|
62
|
+
puts
|
63
|
+
JSON.parse(e.response)['errors'].each do |msg|
|
64
|
+
printf(" * %s\n", msg)
|
65
|
+
end
|
66
|
+
puts
|
67
|
+
exit -1
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def with_valid_site
|
72
|
+
request_credentials unless Stastic::Config.credentials?
|
73
|
+
missing_site_id unless Stastic::Config.site_defined?
|
74
|
+
begin
|
75
|
+
yield
|
76
|
+
rescue RestClient::Forbidden
|
77
|
+
request_credentials
|
78
|
+
retry
|
79
|
+
rescue RestClient::UnprocessableEntity => e
|
80
|
+
puts "Error:"
|
81
|
+
puts e.response.to_s
|
82
|
+
puts
|
83
|
+
exit
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def missing_site_id
|
88
|
+
puts "No Stastic Site found. To create a site, run:"
|
89
|
+
puts
|
90
|
+
puts " stastic create"
|
91
|
+
puts
|
92
|
+
exit
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Stastic::Command
|
2
|
+
class Create < Base
|
3
|
+
attr_accessor :name
|
4
|
+
attr_accessor :template
|
5
|
+
|
6
|
+
def initialize(args = [])
|
7
|
+
case args.size
|
8
|
+
when 0
|
9
|
+
self.name = nil
|
10
|
+
when 1
|
11
|
+
if args.first == "--template"
|
12
|
+
self.template = "default"
|
13
|
+
else
|
14
|
+
self.name = args.first
|
15
|
+
end
|
16
|
+
when 2
|
17
|
+
raise(Stastic::InvalidOptions) unless args.first == "--template"
|
18
|
+
self.template = "default"
|
19
|
+
self.name = args.last
|
20
|
+
puts self.template
|
21
|
+
else
|
22
|
+
raise(Stastic::InvalidOptions)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def index
|
27
|
+
if template
|
28
|
+
copy_template("#{File.dirname(__FILE__)}/../../../templates/#{template}/")
|
29
|
+
end
|
30
|
+
|
31
|
+
response = with_valid_user do
|
32
|
+
Stastic::Client.create(name)
|
33
|
+
end
|
34
|
+
|
35
|
+
if response["id"]
|
36
|
+
Stastic::Config.add(:site_id => response["id"])
|
37
|
+
end
|
38
|
+
|
39
|
+
if response["name"]
|
40
|
+
Stastic::Config.add(:name => response["name"])
|
41
|
+
print "Your site has been created!\n\n"
|
42
|
+
print " Site Name: #{Stastic::Config.name}\n"
|
43
|
+
print " Site URL: #{Stastic::Config.name}.stastic.com\n\n"
|
44
|
+
print "Use `stastic publish` to publish your site whenever you're ready.\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def copy_template(src)
|
52
|
+
dest = Dir.pwd
|
53
|
+
Dir.foreach(src) do |file|
|
54
|
+
s = File.join(src, file)
|
55
|
+
d = File.join(dest, file)
|
56
|
+
|
57
|
+
if File.directory?(s)
|
58
|
+
FileUtils.cp_r(s, d, :preserve => true)
|
59
|
+
else
|
60
|
+
FileUtils.cp(s,d, :preserve => true)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Stastic::Command
|
2
|
+
class Domains < Base
|
3
|
+
attr_accessor :domain_name
|
4
|
+
|
5
|
+
def initialize(args = [])
|
6
|
+
case args.size
|
7
|
+
when 1
|
8
|
+
self.domain_name = args.first
|
9
|
+
raise(Stastic::Command::InvalidOptions) unless validate_domain(domain_name)
|
10
|
+
else
|
11
|
+
raise(Stastic::Command::InvalidOptions)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def index
|
17
|
+
end
|
18
|
+
|
19
|
+
def add
|
20
|
+
print "Adding domain #{domain_name}... "
|
21
|
+
with_valid_site do
|
22
|
+
Stastic::Client.add_domain(Stastic::Config.site_id, domain_name)
|
23
|
+
end
|
24
|
+
puts "Success\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove
|
28
|
+
print "Removing domain #{domain_name}... "
|
29
|
+
with_valid_site do
|
30
|
+
Stastic::Client.remove_domain(Stastic::Config.site_id, domain_name)
|
31
|
+
end
|
32
|
+
puts "Success\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def validate_domain(domain_name)
|
37
|
+
domain_name =~ /^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.){1,3}([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$/
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Stastic::Command
|
2
|
+
class Help < Base
|
3
|
+
|
4
|
+
def index
|
5
|
+
print usage
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def usage
|
11
|
+
<<-EOF
|
12
|
+
stastic (#{Stastic::VERSION}) - command-line tool for managing your Stastic sites
|
13
|
+
|
14
|
+
Available Commands
|
15
|
+
------------------
|
16
|
+
|
17
|
+
stastic help # You're looking at it!
|
18
|
+
stastic create [name] # Create a site (using the current directory)
|
19
|
+
stastic publish # Publish the site
|
20
|
+
stastic info # Show info about the current site
|
21
|
+
stastic list # List sites associated with your account
|
22
|
+
stastic rename <name> # Rename the site (name.stastic.com)
|
23
|
+
stastic domains:add <domain> # Add a domain to the site (example.com)
|
24
|
+
stastic domains:remove <domain> # Remove a domain from the site
|
25
|
+
stastic preview # Start local HTTP server
|
26
|
+
stastic set site_root <path> # Set the local path to the static files
|
27
|
+
|
28
|
+
EOF
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Stastic::Command
|
2
|
+
class Info < Base
|
3
|
+
attr_accessor :name
|
4
|
+
|
5
|
+
def index
|
6
|
+
puts info
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def info
|
12
|
+
generator = Stastic::Generator.detect
|
13
|
+
<<-EOF
|
14
|
+
stastic version : #{Stastic::VERSION}
|
15
|
+
user : #{Stastic::Config.user}
|
16
|
+
active site name : #{Stastic::Config.name}
|
17
|
+
active site id : #{Stastic::Config.site_id}
|
18
|
+
active site url : http://#{Stastic::Config.name}.stastic.com/
|
19
|
+
|
20
|
+
generator : #{generator.desc}
|
21
|
+
site_root : #{generator.site_root}
|
22
|
+
|
23
|
+
EOF
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Stastic::Command
|
2
|
+
class List < Base
|
3
|
+
|
4
|
+
def index
|
5
|
+
list
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def list
|
11
|
+
response = with_valid_user { Stastic::Client.list }
|
12
|
+
|
13
|
+
puts header
|
14
|
+
response['sites'].each do |site|
|
15
|
+
site_info(site, site['id'] == Stastic::Config.site_id ? "*" : " ")
|
16
|
+
end
|
17
|
+
puts
|
18
|
+
end
|
19
|
+
|
20
|
+
def header
|
21
|
+
<<-EOF
|
22
|
+
|
23
|
+
Your Stastic Sites
|
24
|
+
-----------------
|
25
|
+
|
26
|
+
name url
|
27
|
+
---- ---
|
28
|
+
EOF
|
29
|
+
end
|
30
|
+
|
31
|
+
def site_info(site, marker = " ")
|
32
|
+
printf("%-2s%-30s http://%s.stastic.com\n", marker, site['name'], site['name'])
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Stastic::Command
|
2
|
+
class Publish < Base
|
3
|
+
|
4
|
+
def index
|
5
|
+
publish
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
def publish
|
10
|
+
puts "Detecting..."
|
11
|
+
generator = Stastic::Generator.detect
|
12
|
+
puts "Found: #{generator.desc}"
|
13
|
+
|
14
|
+
puts "Building..."
|
15
|
+
generator.build
|
16
|
+
|
17
|
+
puts "Packaging..."
|
18
|
+
archive_path = generator.package
|
19
|
+
|
20
|
+
print "Uploading..."
|
21
|
+
upload(archive_path)
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def upload(archive_path)
|
26
|
+
print "Uploading your archive...\n"
|
27
|
+
reponse = with_valid_site do
|
28
|
+
Stastic::Client.publish(Stastic::Config.site_id, archive_path)
|
29
|
+
end
|
30
|
+
puts "Complete\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Stastic::Command
|
2
|
+
class Rename < Base
|
3
|
+
attr_accessor :new_name
|
4
|
+
|
5
|
+
def initialize(args = [])
|
6
|
+
case args.size
|
7
|
+
when 1
|
8
|
+
self.new_name = args.first
|
9
|
+
else
|
10
|
+
raise(Stastic::Command::InvalidOptions)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def index
|
15
|
+
print "Changing Site Name... "
|
16
|
+
response = with_valid_site do
|
17
|
+
Stastic::Client.rename(Stastic::Config.site_id, new_name)
|
18
|
+
end
|
19
|
+
Stastic::Config.update(:name => response["name"])
|
20
|
+
puts "Success\n"
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Stastic::Command
|
2
|
+
class Set < Base
|
3
|
+
attr_accessor :settings
|
4
|
+
|
5
|
+
VALID_SETTINGS = [:site_root, :site_id]
|
6
|
+
|
7
|
+
def initialize(args = [])
|
8
|
+
case args.size
|
9
|
+
when 0
|
10
|
+
raise(Stastic::Command::InvalidOptions)
|
11
|
+
else
|
12
|
+
self.settings = parse_args(args)
|
13
|
+
validate_settings(settings)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def index
|
18
|
+
update_settings(settings)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def parse_args(args)
|
23
|
+
args.inject(Hash.new) do |h,arg|
|
24
|
+
k,v = arg.split('=')
|
25
|
+
h[k.to_sym] = v
|
26
|
+
h
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate_settings(settings)
|
31
|
+
raise(Stastic::Command::InvalidOptions) unless settings.keys.all? {|key| VALID_SETTINGS.include?(key)}
|
32
|
+
end
|
33
|
+
|
34
|
+
def set_name(name)
|
35
|
+
print "Changing Site Name... "
|
36
|
+
begin
|
37
|
+
response = Stastic::Client.request(:put, "/sites/#{Stastic::Config.site_id}", {:site => {:name => name}})
|
38
|
+
rescue Stastic::InvalidCredentials
|
39
|
+
request_credentials
|
40
|
+
retry
|
41
|
+
rescue Stastic::ValidationFailed
|
42
|
+
print "Could not change site name\n"
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
|
46
|
+
puts "Success\n"
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
def update_settings(settings)
|
51
|
+
Stastic::Config.update(settings)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|