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.
Files changed (116) hide show
  1. data/.document +5 -0
  2. data/.yardopts +7 -0
  3. data/ChangeLog.md +8 -0
  4. data/Gemfile +26 -0
  5. data/Gemfile.lock +30 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.md +52 -0
  8. data/Rakefile +62 -0
  9. data/bin/yuzu +17 -0
  10. data/docs/About.md +19 -0
  11. data/docs/GettingStarted.md +48 -0
  12. data/docs/Reference.md +97 -0
  13. data/lib/helpers/object.rb +19 -0
  14. data/lib/helpers/path.rb +296 -0
  15. data/lib/helpers/string.rb +35 -0
  16. data/lib/helpers/system_checks.rb +10 -0
  17. data/lib/helpers/url.rb +64 -0
  18. data/lib/html/base.rb +187 -0
  19. data/lib/uploader/base.rb +59 -0
  20. data/lib/uploader/config.rb +19 -0
  21. data/lib/uploader/filesystem_service.rb +72 -0
  22. data/lib/uploader/ftp_service.rb +90 -0
  23. data/lib/uploader/s3_service.rb +135 -0
  24. data/lib/uploader/service.rb +57 -0
  25. data/lib/uploader/suppressor.rb +25 -0
  26. data/lib/yuzu.rb +6 -0
  27. data/lib/yuzu/argparse.rb +60 -0
  28. data/lib/yuzu/command.rb +104 -0
  29. data/lib/yuzu/commands/base.rb +150 -0
  30. data/lib/yuzu/commands/create.rb +68 -0
  31. data/lib/yuzu/commands/generate.rb +95 -0
  32. data/lib/yuzu/commands/help.rb +20 -0
  33. data/lib/yuzu/commands/preview.rb +58 -0
  34. data/lib/yuzu/commands/publish.rb +43 -0
  35. data/lib/yuzu/commands/stage.rb +62 -0
  36. data/lib/yuzu/commands/watch.rb +70 -0
  37. data/lib/yuzu/content/blog_post.rb +45 -0
  38. data/lib/yuzu/content/sample_project.rb +130 -0
  39. data/lib/yuzu/core/config.rb +154 -0
  40. data/lib/yuzu/core/layout.rb +85 -0
  41. data/lib/yuzu/core/paginated_file.rb +69 -0
  42. data/lib/yuzu/core/registrar.rb +32 -0
  43. data/lib/yuzu/core/siteroot.rb +57 -0
  44. data/lib/yuzu/core/template.rb +158 -0
  45. data/lib/yuzu/core/updater.rb +123 -0
  46. data/lib/yuzu/core/visitor.rb +44 -0
  47. data/lib/yuzu/core/website_base.rb +150 -0
  48. data/lib/yuzu/core/website_file.rb +270 -0
  49. data/lib/yuzu/core/website_folder.rb +176 -0
  50. data/lib/yuzu/filters/base.rb +86 -0
  51. data/lib/yuzu/filters/catalog.rb +248 -0
  52. data/lib/yuzu/filters/categories.rb +58 -0
  53. data/lib/yuzu/filters/currentpath.rb +35 -0
  54. data/lib/yuzu/filters/description.rb +16 -0
  55. data/lib/yuzu/filters/extension.rb +16 -0
  56. data/lib/yuzu/filters/images.rb +32 -0
  57. data/lib/yuzu/filters/linkroot.rb +35 -0
  58. data/lib/yuzu/filters/post_date.rb +45 -0
  59. data/lib/yuzu/filters/post_title.rb +66 -0
  60. data/lib/yuzu/filters/post_title_removed.rb +28 -0
  61. data/lib/yuzu/filters/sidebar.rb +26 -0
  62. data/lib/yuzu/filters/template.rb +16 -0
  63. data/lib/yuzu/generators/base.rb +44 -0
  64. data/lib/yuzu/generators/category_folders.rb +91 -0
  65. data/lib/yuzu/generators/index.rb +108 -0
  66. data/lib/yuzu/generators/paginate.rb +136 -0
  67. data/lib/yuzu/postprocessors/all_categories.rb +48 -0
  68. data/lib/yuzu/postprocessors/base.rb +34 -0
  69. data/lib/yuzu/postprocessors/contents_without_first_paragraph.rb +20 -0
  70. data/lib/yuzu/postprocessors/excerpt.rb +23 -0
  71. data/lib/yuzu/postprocessors/first_paragraph.rb +16 -0
  72. data/lib/yuzu/postprocessors/pagination.rb +35 -0
  73. data/lib/yuzu/postprocessors/recent_posts.rb +27 -0
  74. data/lib/yuzu/postprocessors/thumbnails.rb +48 -0
  75. data/lib/yuzu/preprocessors/base.rb +71 -0
  76. data/lib/yuzu/preprocessors/insert_contents.rb +57 -0
  77. data/lib/yuzu/renderers/base.rb +23 -0
  78. data/lib/yuzu/renderers/breadcrumb.rb +163 -0
  79. data/lib/yuzu/renderers/gallery.rb +24 -0
  80. data/lib/yuzu/renderers/title.rb +21 -0
  81. data/lib/yuzu/translators/base.rb +57 -0
  82. data/lib/yuzu/translators/markdown.rb +21 -0
  83. data/lib/yuzu/translators/plaintext.rb +17 -0
  84. data/lib/yuzu/version.rb +12 -0
  85. data/resources/config/compass.rb +6 -0
  86. data/resources/config/yuzu.yml +166 -0
  87. data/resources/git/post-commit +42 -0
  88. data/resources/sample_content/introduction/_snippets/about_insert_contents.md +3 -0
  89. data/resources/sample_content/introduction/about.md +6 -0
  90. data/resources/sample_content/introduction/advanced-posts.md +18 -0
  91. data/resources/sample_content/introduction/blog/blog-folder-is-special.md +8 -0
  92. data/resources/sample_content/introduction/getting-started.md +14 -0
  93. data/resources/sample_content/introduction/index.md +6 -0
  94. data/resources/sample_content/introduction/sample-post.md +13 -0
  95. data/resources/sample_projects.yml +7 -0
  96. data/resources/themes/minimal/_sass/print.sass +0 -0
  97. data/resources/themes/minimal/_sass/screen.sass +81 -0
  98. data/resources/themes/minimal/_templates/_block.haml +4 -0
  99. data/resources/themes/minimal/_templates/_blog.haml +3 -0
  100. data/resources/themes/minimal/_templates/_footer.haml +2 -0
  101. data/resources/themes/minimal/_templates/_gallery.haml +25 -0
  102. data/resources/themes/minimal/_templates/_head.haml +12 -0
  103. data/resources/themes/minimal/_templates/_header.haml +1 -0
  104. data/resources/themes/minimal/_templates/_menu.haml +6 -0
  105. data/resources/themes/minimal/_templates/blog.haml +21 -0
  106. data/resources/themes/minimal/_templates/generic.haml +26 -0
  107. data/resources/themes/minimal/_templates/home.haml +15 -0
  108. data/resources/themes/minimal/_templates/index.haml +21 -0
  109. data/resources/themes/minimal/css/print.css +0 -0
  110. data/resources/themes/minimal/css/screen.css +133 -0
  111. data/resources/themes/minimal/img/favicon.png +0 -0
  112. data/resources/yard/default/fulldoc/html/css/common.css +16 -0
  113. data/test/helper.rb +18 -0
  114. data/test/test_yuzu.rb +8 -0
  115. data/yuzu.gemspec +182 -0
  116. 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,6 @@
1
+ #!/usr/bin/env ruby -wKU
2
+
3
+ module Yuzu; end
4
+
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/yuzu')
6
+
@@ -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
+
@@ -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
+