yuzu 0.2.1.pre
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/.document +5 -0
- data/.yardopts +7 -0
- data/ChangeLog.md +8 -0
- data/Gemfile +26 -0
- data/Gemfile.lock +30 -0
- data/LICENSE.txt +20 -0
- data/README.md +52 -0
- data/Rakefile +62 -0
- data/bin/yuzu +17 -0
- data/docs/About.md +19 -0
- data/docs/GettingStarted.md +48 -0
- data/docs/Reference.md +97 -0
- data/lib/helpers/object.rb +19 -0
- data/lib/helpers/path.rb +296 -0
- data/lib/helpers/string.rb +35 -0
- data/lib/helpers/system_checks.rb +10 -0
- data/lib/helpers/url.rb +64 -0
- data/lib/html/base.rb +187 -0
- data/lib/uploader/base.rb +59 -0
- data/lib/uploader/config.rb +19 -0
- data/lib/uploader/filesystem_service.rb +72 -0
- data/lib/uploader/ftp_service.rb +90 -0
- data/lib/uploader/s3_service.rb +135 -0
- data/lib/uploader/service.rb +57 -0
- data/lib/uploader/suppressor.rb +25 -0
- data/lib/yuzu.rb +6 -0
- data/lib/yuzu/argparse.rb +60 -0
- data/lib/yuzu/command.rb +104 -0
- data/lib/yuzu/commands/base.rb +150 -0
- data/lib/yuzu/commands/create.rb +68 -0
- data/lib/yuzu/commands/generate.rb +95 -0
- data/lib/yuzu/commands/help.rb +20 -0
- data/lib/yuzu/commands/preview.rb +58 -0
- data/lib/yuzu/commands/publish.rb +43 -0
- data/lib/yuzu/commands/stage.rb +62 -0
- data/lib/yuzu/commands/watch.rb +70 -0
- data/lib/yuzu/content/blog_post.rb +45 -0
- data/lib/yuzu/content/sample_project.rb +130 -0
- data/lib/yuzu/core/config.rb +154 -0
- data/lib/yuzu/core/layout.rb +85 -0
- data/lib/yuzu/core/paginated_file.rb +69 -0
- data/lib/yuzu/core/registrar.rb +32 -0
- data/lib/yuzu/core/siteroot.rb +57 -0
- data/lib/yuzu/core/template.rb +158 -0
- data/lib/yuzu/core/updater.rb +123 -0
- data/lib/yuzu/core/visitor.rb +44 -0
- data/lib/yuzu/core/website_base.rb +150 -0
- data/lib/yuzu/core/website_file.rb +270 -0
- data/lib/yuzu/core/website_folder.rb +176 -0
- data/lib/yuzu/filters/base.rb +86 -0
- data/lib/yuzu/filters/catalog.rb +248 -0
- data/lib/yuzu/filters/categories.rb +58 -0
- data/lib/yuzu/filters/currentpath.rb +35 -0
- data/lib/yuzu/filters/description.rb +16 -0
- data/lib/yuzu/filters/extension.rb +16 -0
- data/lib/yuzu/filters/images.rb +32 -0
- data/lib/yuzu/filters/linkroot.rb +35 -0
- data/lib/yuzu/filters/post_date.rb +45 -0
- data/lib/yuzu/filters/post_title.rb +66 -0
- data/lib/yuzu/filters/post_title_removed.rb +28 -0
- data/lib/yuzu/filters/sidebar.rb +26 -0
- data/lib/yuzu/filters/template.rb +16 -0
- data/lib/yuzu/generators/base.rb +44 -0
- data/lib/yuzu/generators/category_folders.rb +91 -0
- data/lib/yuzu/generators/index.rb +108 -0
- data/lib/yuzu/generators/paginate.rb +136 -0
- data/lib/yuzu/postprocessors/all_categories.rb +48 -0
- data/lib/yuzu/postprocessors/base.rb +34 -0
- data/lib/yuzu/postprocessors/contents_without_first_paragraph.rb +20 -0
- data/lib/yuzu/postprocessors/excerpt.rb +23 -0
- data/lib/yuzu/postprocessors/first_paragraph.rb +16 -0
- data/lib/yuzu/postprocessors/pagination.rb +35 -0
- data/lib/yuzu/postprocessors/recent_posts.rb +27 -0
- data/lib/yuzu/postprocessors/thumbnails.rb +48 -0
- data/lib/yuzu/preprocessors/base.rb +71 -0
- data/lib/yuzu/preprocessors/insert_contents.rb +57 -0
- data/lib/yuzu/renderers/base.rb +23 -0
- data/lib/yuzu/renderers/breadcrumb.rb +163 -0
- data/lib/yuzu/renderers/gallery.rb +24 -0
- data/lib/yuzu/renderers/title.rb +21 -0
- data/lib/yuzu/translators/base.rb +57 -0
- data/lib/yuzu/translators/markdown.rb +21 -0
- data/lib/yuzu/translators/plaintext.rb +17 -0
- data/lib/yuzu/version.rb +12 -0
- data/resources/config/compass.rb +6 -0
- data/resources/config/yuzu.yml +166 -0
- data/resources/git/post-commit +42 -0
- data/resources/sample_content/introduction/_snippets/about_insert_contents.md +3 -0
- data/resources/sample_content/introduction/about.md +6 -0
- data/resources/sample_content/introduction/advanced-posts.md +18 -0
- data/resources/sample_content/introduction/blog/blog-folder-is-special.md +8 -0
- data/resources/sample_content/introduction/getting-started.md +14 -0
- data/resources/sample_content/introduction/index.md +6 -0
- data/resources/sample_content/introduction/sample-post.md +13 -0
- data/resources/sample_projects.yml +7 -0
- data/resources/themes/minimal/_sass/print.sass +0 -0
- data/resources/themes/minimal/_sass/screen.sass +81 -0
- data/resources/themes/minimal/_templates/_block.haml +4 -0
- data/resources/themes/minimal/_templates/_blog.haml +3 -0
- data/resources/themes/minimal/_templates/_footer.haml +2 -0
- data/resources/themes/minimal/_templates/_gallery.haml +25 -0
- data/resources/themes/minimal/_templates/_head.haml +12 -0
- data/resources/themes/minimal/_templates/_header.haml +1 -0
- data/resources/themes/minimal/_templates/_menu.haml +6 -0
- data/resources/themes/minimal/_templates/blog.haml +21 -0
- data/resources/themes/minimal/_templates/generic.haml +26 -0
- data/resources/themes/minimal/_templates/home.haml +15 -0
- data/resources/themes/minimal/_templates/index.haml +21 -0
- data/resources/themes/minimal/css/print.css +0 -0
- data/resources/themes/minimal/css/screen.css +133 -0
- data/resources/themes/minimal/img/favicon.png +0 -0
- data/resources/yard/default/fulldoc/html/css/common.css +16 -0
- data/test/helper.rb +18 -0
- data/test/test_yuzu.rb +8 -0
- data/yuzu.gemspec +182 -0
- metadata +302 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
require 'uploader/base'
|
|
2
|
+
require 'uploader/service'
|
|
3
|
+
require 'net/ftp'
|
|
4
|
+
require 'stringio'
|
|
5
|
+
|
|
6
|
+
module Uploader
|
|
7
|
+
class FtpService < Service
|
|
8
|
+
|
|
9
|
+
def connect!
|
|
10
|
+
$stderr.puts "Opening a persistent FTP connection to #{@config.host}..."
|
|
11
|
+
|
|
12
|
+
@ftp = Net::FTP.new
|
|
13
|
+
@ftp.passive = true
|
|
14
|
+
@ftp.connect(@config.host)
|
|
15
|
+
@ftp.login(@config.username, @config.password)
|
|
16
|
+
rescue
|
|
17
|
+
$strerr.puts "Something went wrong with the FTP login."
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def close!
|
|
21
|
+
@ftp.close
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def initialized?
|
|
25
|
+
not @ftp.nil?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def upload(remote_path, contents)
|
|
29
|
+
raise UploadArgumentError if not remote_path.is_a?(Path)
|
|
30
|
+
|
|
31
|
+
if contents.is_a?(File)
|
|
32
|
+
upload_via_ftp(remote_path, contents)
|
|
33
|
+
|
|
34
|
+
elsif contents.is_a?(String)
|
|
35
|
+
upload_contents_via_ftp(remote_path, contents)
|
|
36
|
+
|
|
37
|
+
else
|
|
38
|
+
raise UnrecognizedContentType
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def upload_contents_via_ftp(remote_path, contents)
|
|
44
|
+
file = StringIO.new(contents)
|
|
45
|
+
upload_via_ftp(remote_path, file, false)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def upload_via_ftp(remote_path, file, binary=true)
|
|
49
|
+
server_path = Path.new(@config.remote_root_path) + remote_path.relative
|
|
50
|
+
|
|
51
|
+
$stderr.puts "Opening an FTP connection for #{remote_path}"
|
|
52
|
+
|
|
53
|
+
begin
|
|
54
|
+
attempt_upload!(server_path, file, binary)
|
|
55
|
+
rescue => e
|
|
56
|
+
$stderr.puts "FTP Error"
|
|
57
|
+
$stderr.puts e.message
|
|
58
|
+
|
|
59
|
+
create_remote_folder!(server_path)
|
|
60
|
+
attempt_upload!(server_path, file, binary)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def attempt_upload!(server_path, file, binary)
|
|
65
|
+
$stderr.puts "Path on server: #{server_path}"
|
|
66
|
+
if binary
|
|
67
|
+
file.binmode
|
|
68
|
+
@ftp.storbinary("STOR #{server_path}", file, Net::FTP::DEFAULT_BLOCKSIZE)
|
|
69
|
+
else
|
|
70
|
+
@ftp.storlines("STOR #{server_path}", file)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def create_remote_folder!(server_path)
|
|
75
|
+
server_path.descend do |path|
|
|
76
|
+
begin
|
|
77
|
+
@ftp.mkdir(path)
|
|
78
|
+
rescue Net::FTPPermError
|
|
79
|
+
$stderr.puts "Remote folder already exists."
|
|
80
|
+
rescue
|
|
81
|
+
# Ok.
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
Service.register(:ftp => FtpService)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
require 'helpers/system_checks'
|
|
2
|
+
require 'uploader/base'
|
|
3
|
+
require 'uploader/service'
|
|
4
|
+
require 'uploader/suppressor'
|
|
5
|
+
|
|
6
|
+
module Uploader
|
|
7
|
+
class S3Service < Service
|
|
8
|
+
|
|
9
|
+
def connect!
|
|
10
|
+
# TODO S3 may not work yet.
|
|
11
|
+
|
|
12
|
+
if not SystemChecks.gem_available?('aws/s3')
|
|
13
|
+
$stderr.puts %Q{\nThe Amazon S3 service requires the aws-s3 gem (vers >= 0.6.2), which is
|
|
14
|
+
not required by default. Run 'gem install aws-s3' to install it.\n\n}
|
|
15
|
+
raise LoadError
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@bucket_name = @config.bucket
|
|
19
|
+
access_key = ENV[@config.access_key]
|
|
20
|
+
secret_key = ENV[@config.secret_key]
|
|
21
|
+
|
|
22
|
+
$stderr.puts "Connecting to AWS..."
|
|
23
|
+
|
|
24
|
+
@suppressor.shutup!
|
|
25
|
+
|
|
26
|
+
unless AWS::S3::Base.connected?
|
|
27
|
+
options = {
|
|
28
|
+
:access_key_id => access_key,
|
|
29
|
+
:secret_access_key => secret_key,
|
|
30
|
+
:use_ssl => true,
|
|
31
|
+
:port => 443
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if @config.use_proxy
|
|
35
|
+
options.update({
|
|
36
|
+
:proxy => {
|
|
37
|
+
:host => @config.proxy['host'],
|
|
38
|
+
:port => @config.proxy['port']
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
AWS::S3::Base.establish_connection!(options)
|
|
44
|
+
@suppressor.ok
|
|
45
|
+
|
|
46
|
+
else
|
|
47
|
+
@suppressor.ok
|
|
48
|
+
$stderr.puts "AWS already connected."
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
@s3_bucket = AWS::S3::Bucket.find(@bucket_name)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def close!
|
|
55
|
+
AWS::S3::Base.disconnect!
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def upload(remote_path, contents)
|
|
59
|
+
raise UploadArgumentError if not remote_path.is_a?(Path)
|
|
60
|
+
|
|
61
|
+
if contents.is_a?(File)
|
|
62
|
+
contents_to_upload = contents.read
|
|
63
|
+
|
|
64
|
+
elsif contents.is_a?(String)
|
|
65
|
+
contents = contents
|
|
66
|
+
|
|
67
|
+
else
|
|
68
|
+
raise UnrecognizedContentType
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
upload_contents_to_s3(local_path, contents_to_upload)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def upload_contents_to_s3(remote_path, contents, mime_type=nil)
|
|
76
|
+
$stderr.puts "Attempting to upload #{remote_path} to S3"
|
|
77
|
+
|
|
78
|
+
destination = Path.new(@config.remote_root_path) + remote_path
|
|
79
|
+
|
|
80
|
+
@suppressor.shutup!
|
|
81
|
+
|
|
82
|
+
# Prepare the object.
|
|
83
|
+
object = @s3_bucket.new_object
|
|
84
|
+
object.key = destination
|
|
85
|
+
object.value = contents
|
|
86
|
+
|
|
87
|
+
# Store the object first...
|
|
88
|
+
object.store
|
|
89
|
+
|
|
90
|
+
#if not mime_type.blank?
|
|
91
|
+
# #object.content_type = mime_type
|
|
92
|
+
# #object.store
|
|
93
|
+
#else
|
|
94
|
+
# ext = File.extname(local_path)
|
|
95
|
+
# if content_types.keys.include? ext
|
|
96
|
+
# #object.content_type = content_types[ext]
|
|
97
|
+
# #object.store
|
|
98
|
+
# end
|
|
99
|
+
#end
|
|
100
|
+
|
|
101
|
+
# ... then set the ACLs.
|
|
102
|
+
public_read_grant = AWS::S3::ACL::Grant.grant(:public_read)
|
|
103
|
+
object.acl.grants << public_read_grant
|
|
104
|
+
object.acl(object.acl)
|
|
105
|
+
|
|
106
|
+
@suppressor.ok
|
|
107
|
+
|
|
108
|
+
if @s3_bucket.objects.include?(object)
|
|
109
|
+
$stderr.puts "Successfully uploaded #{local_path} to S3!"
|
|
110
|
+
else
|
|
111
|
+
$stderr.puts "Something went wrong uploading #{local_path} to S3."
|
|
112
|
+
end
|
|
113
|
+
rescue => detail
|
|
114
|
+
$stderr.puts "Uploader#upload_contents_to_s3 exception."
|
|
115
|
+
$stderr.puts detail.message
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.content_types
|
|
119
|
+
{
|
|
120
|
+
".png" => "image/png",
|
|
121
|
+
".jpg" => "image/jpeg",
|
|
122
|
+
".jpeg" => "image/jpeg",
|
|
123
|
+
".pdf" => "application/pdf",
|
|
124
|
+
".jar" => "application/java-archive",
|
|
125
|
+
".txt" => "text/plain",
|
|
126
|
+
".html" => "text/html",
|
|
127
|
+
".css" => "text/css",
|
|
128
|
+
".js" => "text/javascript"
|
|
129
|
+
}
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
end
|
|
133
|
+
Service.register(:s3 => S3Service)
|
|
134
|
+
end
|
|
135
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'helpers/path'
|
|
2
|
+
require 'helpers/system_checks'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
module Uploader
|
|
6
|
+
class UnrecognizedContentType < Exception; end
|
|
7
|
+
class ServiceNotConnected < Exception; end
|
|
8
|
+
class UploadArgumentError < Exception; end
|
|
9
|
+
|
|
10
|
+
class Service
|
|
11
|
+
include Helpers
|
|
12
|
+
|
|
13
|
+
def initialize(config)
|
|
14
|
+
@config = config
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Called to connect to remote services like FTP and S3.
|
|
18
|
+
def connect!
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Perform the upload operation to this service.
|
|
22
|
+
#
|
|
23
|
+
# @param [Path] remote_path The relative path to upload. This is prefixed by "remote_root_path"
|
|
24
|
+
# set in the uploader Config. You can set remote_root_path to "" if you want complete control
|
|
25
|
+
# over the path.
|
|
26
|
+
# @param [File, String] contents The contents to upload into the remote file
|
|
27
|
+
# destination.
|
|
28
|
+
# @return nothing
|
|
29
|
+
def upload(remote_path, contents)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Closes the service connection, if required.
|
|
33
|
+
def close!
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def initialized?
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
@@services = {}
|
|
41
|
+
|
|
42
|
+
def self.services
|
|
43
|
+
@@services
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Returns whether this service appears in the Service class's registry.
|
|
47
|
+
def self.is_registered?(service)
|
|
48
|
+
@@services.keys.include?(service)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Registers the Service in the registry, so clients of the service can access it by name.
|
|
52
|
+
def self.register(kwds={})
|
|
53
|
+
@@services.update(kwds)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Haml and AWS put out a lot of junk. Redirect stdout and stderr to /dev/null to suppress these
|
|
2
|
+
# useless warnings, then restore stdout and stderr at the end.
|
|
3
|
+
class Suppressor
|
|
4
|
+
def initialize
|
|
5
|
+
@hole = File.new('/dev/null', 'w')
|
|
6
|
+
@orig_stdout = $stdout
|
|
7
|
+
@orig_stderr = $stderr
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Redirect stdout to /dev/null
|
|
11
|
+
def shutup!
|
|
12
|
+
$stdout = @hole
|
|
13
|
+
$stderr = @hole
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Restore stdout and stderr
|
|
17
|
+
def ok
|
|
18
|
+
$stdout = @orig_stdout
|
|
19
|
+
$stderr = @orig_stderr
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def close!
|
|
23
|
+
@hole.close
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/yuzu.rb
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'ostruct'
|
|
2
|
+
require 'optparse'
|
|
3
|
+
require 'version'
|
|
4
|
+
|
|
5
|
+
module Yuzu::Command
|
|
6
|
+
HELP_MESSAGE = %Q{
|
|
7
|
+
This is yuzu, a blog-aware, static-website generator and publisher that converts
|
|
8
|
+
a folder of text files and images into an HTML5 website.
|
|
9
|
+
|
|
10
|
+
Typical Usage
|
|
11
|
+
|
|
12
|
+
yuzu [command] [options] [files]
|
|
13
|
+
|
|
14
|
+
Available Commands
|
|
15
|
+
|
|
16
|
+
help # Show this help page.
|
|
17
|
+
help [command] # Help about the particular command.
|
|
18
|
+
create # Creates a new yuzu project in the current folder.
|
|
19
|
+
preview # Updates files in the preview folder.
|
|
20
|
+
publish # Publishes files to the currently selected remote server.
|
|
21
|
+
stage # Updates files in the staging folder.
|
|
22
|
+
generate # Generate new content like posts and thumbnails.
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class ArgParser
|
|
26
|
+
def self.parse(args)
|
|
27
|
+
options = OpenStruct.new
|
|
28
|
+
options.output = :quiet # :verbose, :debug
|
|
29
|
+
|
|
30
|
+
option_parser = \
|
|
31
|
+
OptionParser.new do |opts|
|
|
32
|
+
opts.banner = ""
|
|
33
|
+
|
|
34
|
+
opts.separator "Options"
|
|
35
|
+
opts.separator ""
|
|
36
|
+
|
|
37
|
+
opts.on("--verbose", "Run showing verbose output.") do
|
|
38
|
+
options.output = :verbose
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
opts.on_tail("-h", "--help", "Show this help message.") do
|
|
42
|
+
puts HELP_MESSAGE
|
|
43
|
+
puts opts
|
|
44
|
+
exit
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
opts.on_tail("-v", "--version", "Show version.") do
|
|
48
|
+
puts Yuzu::Version::STRING
|
|
49
|
+
exit
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
option_parser.parse!(args)
|
|
54
|
+
options
|
|
55
|
+
|
|
56
|
+
end # End self.parse
|
|
57
|
+
end # End ArgParser
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
data/lib/yuzu/command.rb
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
|
|
3
|
+
require 'commands/base'
|
|
4
|
+
require 'argparse'
|
|
5
|
+
|
|
6
|
+
require 'helpers/object'
|
|
7
|
+
require 'helpers/string'
|
|
8
|
+
|
|
9
|
+
# Load all the commands.
|
|
10
|
+
Dir["#{File.dirname(__FILE__)}/commands/*"].each { |c| require c }
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
CONFIG_NOT_FOUND_MESSAGE = \
|
|
14
|
+
%Q{Please run this from the root of your project, in the folder containing
|
|
15
|
+
yuzu.yml or config/yuzu.yml. Or use 'create' to make a new project.}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
module Yuzu::Command
|
|
19
|
+
|
|
20
|
+
def self.run(command_str, args)
|
|
21
|
+
start = Time.now
|
|
22
|
+
|
|
23
|
+
options = ArgParser.parse(args)
|
|
24
|
+
|
|
25
|
+
if command_str.nil?
|
|
26
|
+
exit
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
command_class, method = parse(command_str.downcase)
|
|
30
|
+
execute(command_class, method, args[1, args.length] || [], options)
|
|
31
|
+
|
|
32
|
+
stop = Time.now
|
|
33
|
+
delta = stop - start
|
|
34
|
+
if options.output == :verbose
|
|
35
|
+
$stderr.puts "Yuzu completed in #{delta} seconds."
|
|
36
|
+
else
|
|
37
|
+
$stderr.puts
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.parse(command_str)
|
|
42
|
+
command, method = command_str.split(':').collect {|part| part.strip}
|
|
43
|
+
method = method.nil? ? :index : method.to_sym
|
|
44
|
+
|
|
45
|
+
return Yuzu::Command.const_get(command.capitalize), method
|
|
46
|
+
rescue
|
|
47
|
+
raise InvalidCommand
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.execute(command_class, method, args, parsed_options)
|
|
51
|
+
if command_class.requires_config?
|
|
52
|
+
config_hash = load_config
|
|
53
|
+
|
|
54
|
+
config = \
|
|
55
|
+
Yuzu::Core::Config.new(
|
|
56
|
+
config_hash,
|
|
57
|
+
command_class.service_override,
|
|
58
|
+
parsed_options
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
command_instance = command_class.new(args, config)
|
|
62
|
+
command_instance.send(method)
|
|
63
|
+
|
|
64
|
+
else
|
|
65
|
+
command_instance = command_class.new(args)
|
|
66
|
+
command_instance.send(method)
|
|
67
|
+
end
|
|
68
|
+
rescue => e
|
|
69
|
+
$stderr.puts e.message
|
|
70
|
+
$stderr.puts e.backtrace
|
|
71
|
+
raise CommandFailed
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.load_config
|
|
75
|
+
config_location = locate_config
|
|
76
|
+
|
|
77
|
+
if config_location.nil?
|
|
78
|
+
$stderr.puts CONFIG_NOT_FOUND_MESSAGE
|
|
79
|
+
Process.exit!(false)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
YAML.load_file(config_location)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.locate_config
|
|
86
|
+
possible_config_locations.each do |path|
|
|
87
|
+
if File.exists?(path)
|
|
88
|
+
return path
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
return nil
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.possible_config_locations
|
|
95
|
+
[
|
|
96
|
+
File.join(Dir.pwd, "yuzu.yml"),
|
|
97
|
+
File.join(Dir.pwd, "config", "yuzu.yml")
|
|
98
|
+
]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class InvalidCommand < RuntimeError; end
|
|
102
|
+
class CommandFailed < RuntimeError; end
|
|
103
|
+
end
|
|
104
|
+
|