stastic 0.1.0 → 0.2.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.
- 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
|