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,150 @@
1
+ require 'helpers/path'
2
+
3
+ module Yuzu::Core
4
+ class WebsiteBase
5
+ include Helpers
6
+
7
+ attr_reader :path, :parent, :kind
8
+
9
+ def initialize(path, parent)
10
+ raise "WebsiteBase not initialized with a Path object." if not path.is_a?(Path)
11
+
12
+ @path = path
13
+ raise "@path is nil for #{self}" if @path.nil?
14
+ @parent = parent
15
+ @kind = nil
16
+ @stash = {}
17
+ end
18
+
19
+ def == (other)
20
+ return false if not other.is_a?(WebsiteBase)
21
+ return @path == other.path
22
+ end
23
+
24
+ def name
25
+ @path.basename
26
+ end
27
+
28
+ def root
29
+ @root ||= (@parent.nil? ? self : @parent.root)
30
+ end
31
+
32
+ def root?
33
+ root == self
34
+ end
35
+
36
+ def blog_folder
37
+ @@blog ||= root.find_file_by_path(Path.new(config.blog_dir))
38
+ end
39
+
40
+ def in_blog?
41
+ blog_folder.nil? ? false : blog_folder.is_child?(self)
42
+ end
43
+
44
+ def is_blog?
45
+ self == blog_folder or (index? and self.path.dirname == blog_folder.path.dirname)
46
+ end
47
+
48
+ def index?
49
+ false
50
+ end
51
+
52
+ def config
53
+ @parent.config
54
+ end
55
+
56
+ def file?
57
+ @kind == :file
58
+ end
59
+
60
+ def folder?
61
+ @kind == :folder
62
+ end
63
+
64
+ def children
65
+ nil
66
+ end
67
+
68
+ def processable?
69
+ @processable ||= is_processable?
70
+ end
71
+
72
+ def is_processable?
73
+ return false if folder?
74
+ config_says = config.processable?(@path)
75
+ translators_say = Yuzu::Translators::Translator.can_translate?(self)
76
+ config_says and translators_say
77
+ end
78
+
79
+ def resource?
80
+ return false if folder?
81
+ config.resource?(@path)
82
+ end
83
+
84
+ def image?
85
+ return false if folder?
86
+ config.image?(@path)
87
+ end
88
+
89
+ def asset?
90
+ return false if folder?
91
+ config.asset?(@path)
92
+ end
93
+
94
+ def hidden?
95
+ return false if root?
96
+ @parent.hidden? or @path.rootname[0].chr == "_"
97
+ end
98
+
99
+ def config
100
+ @parent.config
101
+ end
102
+
103
+ def link_to_self(attr={})
104
+ Html::Link.new({:href => link_url}.merge(attr)) << name
105
+ end
106
+
107
+ def link_url
108
+ @link_url ||= get_link_url
109
+ end
110
+
111
+ def get_link_url
112
+ folder? ? currentpath.full : (currentpath + output_filename).full
113
+ end
114
+
115
+ def remote_path
116
+ @remote_path ||= get_remote_path
117
+ end
118
+
119
+ def get_remote_path
120
+ if processable?
121
+ tr = file? ? @path.with_extension(extension) : @path
122
+ if tr == @path
123
+ raise "Remote path and source path are the same! Got #{tr} and #{@path}."
124
+ end
125
+ tr
126
+ else
127
+ @path
128
+ end
129
+ end
130
+
131
+ def default_stash
132
+ {
133
+ :generated_siblings => [],
134
+ :catalog => nil
135
+ }
136
+ end
137
+
138
+ # Enables other processes to add imbue this node with information.
139
+ def stash(kwds={})
140
+ @stash = default_stash if @stash.nil?
141
+ kwds.empty? ? @stash : @stash.update(kwds)
142
+ end
143
+
144
+ def generated?
145
+ false
146
+ end
147
+
148
+ end
149
+ end
150
+
@@ -0,0 +1,270 @@
1
+ require 'html/base'
2
+ require 'core/website_base'
3
+ require 'core/layout'
4
+
5
+ require 'filters/base'
6
+ require 'renderers/base'
7
+ require 'preprocessors/base'
8
+ require 'postprocessors/base'
9
+ require 'translators/base'
10
+
11
+ %w(filters renderers preprocessors postprocessors translators).each do |folder|
12
+ Dir["#{File.dirname(__FILE__)}/../#{folder}/*"].each { |c| require c if not c.include?("base.rb")}
13
+ end
14
+
15
+ module Yuzu::Core
16
+ class WebsiteFile < WebsiteBase
17
+ include Yuzu::Filters
18
+ include Yuzu::Renderers
19
+ include Yuzu::PreProcessors
20
+ include Yuzu::PostProcessors
21
+ include Yuzu::Translators
22
+
23
+ def self.translators
24
+ Translator.translators
25
+ end
26
+
27
+ def self.processors
28
+ tr = Filter.filters.merge(Renderer.renderers)
29
+ end
30
+
31
+ # Create an accessor method for each filter, renderer, postprocessor.
32
+ self.processors.each_pair do |name, object|
33
+ execution_method = object.kind_of?(Renderer) ? :render : :value
34
+
35
+ instance_variable = "@#{name}".to_sym
36
+
37
+ define_method(name) do
38
+ val = self.instance_variable_get(instance_variable)
39
+
40
+ if val.nil?
41
+ self.instance_variable_set(
42
+ instance_variable,
43
+ object.send(execution_method, self)
44
+ )
45
+ end
46
+
47
+ self.instance_variable_get(instance_variable)
48
+ end
49
+ end
50
+
51
+ # Adds all the postprocessors to this object as methods.
52
+ PostProcessor.postprocessors.each_pair do |name, processor|
53
+
54
+ define_method name do
55
+ # This routine caches the result in an instance variable.
56
+ instance_variable = "@postprocessor_#{name}".to_sym
57
+ begin
58
+ if instance_variable_get(instance_variable).nil?
59
+ result = processor.value(self)
60
+ instance_variable_set(instance_variable, result)
61
+ end
62
+ instance_variable_get(instance_variable)
63
+ rescue => e
64
+ puts "\033[91mEXCEPTION IN #{name}\033[0m"
65
+ #puts e.message
66
+ #puts e.backtrace
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ attr_reader :page, :parent, :path
73
+
74
+ def initialize(path, parent, page=1)
75
+ @path = path
76
+ raise "@path is nil for #{self}" if @path.nil?
77
+
78
+ @parent = parent
79
+ @page = page
80
+
81
+ @kind = :file
82
+ end
83
+
84
+ # Returns a Hash of the properties of this file, like the name, etc.
85
+ def properties
86
+ @properties ||= FileProperties.new(self)
87
+ end
88
+
89
+ def to_s
90
+ "WebsiteFile(#{@path.relative})"
91
+ end
92
+
93
+ def name
94
+ post_title
95
+ end
96
+
97
+ def author
98
+ config.author
99
+ end
100
+
101
+ def home?
102
+ parent.root? and index?
103
+ end
104
+
105
+ def index?
106
+ basename == "index" or basename.include?("index_")
107
+ end
108
+
109
+ def filename
110
+ @path.filename
111
+ end
112
+
113
+ def basename
114
+ @path.basename
115
+ end
116
+
117
+ def output_filename
118
+ processable? ? @path.with_extension(extension).filename : filename
119
+ end
120
+
121
+ def markdown?
122
+ @path.markdown?
123
+ end
124
+
125
+ def plaintext?
126
+ @path.plaintext?
127
+ end
128
+
129
+ # Alias
130
+ def link_root
131
+ linkroot
132
+ end
133
+
134
+ def paginated?
135
+ false
136
+ end
137
+
138
+ def raw_contents
139
+ if @raw_contents.nil?
140
+ @raw_contents = get_raw_contents
141
+ preprocess!
142
+ end
143
+ @raw_contents
144
+ end
145
+
146
+ def get_raw_contents
147
+ f = File.open(@path.absolute, "r")
148
+ contents = f.read
149
+ f.close
150
+ contents
151
+ end
152
+
153
+ def created_at
154
+ @created_at ||= @path.pathname.ctime
155
+ end
156
+
157
+ def modified_at
158
+ @modified_at ||= @path.pathname.mtime
159
+ end
160
+
161
+ def preprocess!
162
+ PreProcessor.preprocessors.each do |name, preprocessor|
163
+ preprocessor.process(self, @raw_contents)
164
+ end
165
+ end
166
+
167
+ def prefiltered_contents
168
+ @prefiltered_contents ||= get_prefiltered_contents
169
+ end
170
+
171
+ def get_prefiltered_contents
172
+ tr = raw_contents
173
+ prefilters.each do |filter|
174
+ tr = filter.process(self, tr)
175
+ end
176
+ tr
177
+ end
178
+
179
+ # Returns the contents passed through the filters.
180
+ #
181
+ # @return [String] the processed contents
182
+ def processed_contents
183
+ @processed_contents ||= get_processed_contents
184
+ end
185
+
186
+ def get_processed_contents
187
+ tr = prefiltered_contents
188
+ (mainfilters + postfilters).each do |filter|
189
+ tr = filter.process(self, tr)
190
+ end
191
+ tr
192
+ end
193
+
194
+ # Return the results of running the processed_contents through the textual processors, e.g.
195
+ # markdown.
196
+ #
197
+ # @return [String] the file's html-rendered contents
198
+ def rendered_contents
199
+ @rendered_contents ||= Translator.translate(processed_contents, @path.extension)
200
+ end
201
+
202
+ def layout
203
+ @layout ||= Yuzu::Core::Layout.new(template)
204
+ end
205
+
206
+ # Presents the rendered_contents placed into its layout. The postprocessors act on this to
207
+ # produce the values needed for html_contents.
208
+ def layout_contents
209
+ layout.render(self)
210
+ end
211
+
212
+
213
+ # The user-facing entry for post.contents in the Haml templates.
214
+ def contents
215
+ rendered_contents
216
+ end
217
+
218
+ # Renders the full, rendered HTML to write to disk.
219
+ def html_contents
220
+ layout_contents
221
+ end
222
+
223
+ def filters
224
+ Filter.filters
225
+ end
226
+
227
+ def prefilters
228
+ @prefilters ||= filters.values.select {|filt| filt.filter_type.include?(:prefilter)}
229
+ end
230
+
231
+ def mainfilters
232
+ @mainfilters ||= filters.values.select {|filt| filt.filter_type.include?(:filter)}
233
+ end
234
+
235
+ def postfilters
236
+ @postfilters ||= filters.values.select {|filt| filt.filter_type.include?(:postfilter)}
237
+ end
238
+
239
+ end
240
+
241
+
242
+ # FileProperties objects are passed into renderers when they need to pass information
243
+ # to a Haml template or other user-facing mechanism. e.g. This makes "breadcrumb" available in
244
+ # Haml templates and layouts by using post.breadcrumb.
245
+ class FileProperties
246
+ # instance.methods == public instance methods
247
+ def initialize(website_file)
248
+
249
+ @website_file = website_file
250
+ unique_methods = (website_file.methods - Object.instance_methods).sort
251
+
252
+ (class << self; self; end).class_eval do
253
+
254
+ unique_methods.each do |method_name|
255
+
256
+ define_method(method_name) do
257
+ website_file.send(method_name)
258
+ end
259
+
260
+ end
261
+ end # class_eval
262
+
263
+ end # initialize
264
+
265
+ def to_s
266
+ "Properties(#{@website_file.path.relative})"
267
+ end
268
+ end
269
+
270
+ end
@@ -0,0 +1,176 @@
1
+ require 'core/website_file'
2
+
3
+ module Yuzu::Core
4
+ class WebsiteFolder < WebsiteBase
5
+ def initialize(path, parent)
6
+ raise "Not a Path object." if not path.is_a?(Path)
7
+ @path = path
8
+ raise "@path is nil for #{self}" if @path.nil?
9
+ @parent = parent
10
+ @kind = :folder
11
+ end
12
+
13
+ def to_s
14
+ "WebsiteFolder(#{@path})"
15
+ end
16
+
17
+ # -------------------------------------------------------------------------------------------
18
+ # TODO Find a way for folders to know what all the categories are, or better yet, remove this
19
+ # interface and have another way of files, folders, and processors to ask information about the
20
+ # entire site.
21
+ #def all_categories
22
+ #end
23
+
24
+ def breadcrumb
25
+ Yuzu::Renderers::BreadcrumbRenderer.new.render(self)
26
+ end
27
+
28
+ def currentpath
29
+ Yuzu::Filters::CurrentpathFilter.new.value(self)
30
+ end
31
+ # -------------------------------------------------------------------------------------------
32
+
33
+ def name
34
+ @path.rootname.titlecase
35
+ end
36
+
37
+ def files
38
+ @files ||= children.select {|child| child.file?}
39
+ end
40
+
41
+ def folders
42
+ @folders ||= children.select {|child| child.folder?}
43
+ end
44
+
45
+ def all_files
46
+ @all_files ||= get_all_files
47
+ end
48
+
49
+ def get_all_files
50
+ v = Visitor.new(proc {|c| c.file?})
51
+ tr = []
52
+ v.traverse(self) do |website_file|
53
+ tr.push(website_file)
54
+ end
55
+ tr
56
+ end
57
+
58
+ def processable_children
59
+ children.select {|child| child.processable?}
60
+ end
61
+
62
+ # Returns all the descendants of this folder, as deep as the tree goes, that are "processable"
63
+ # as specified by the config.
64
+ #
65
+ # @return [Array] Of WebsiteFiles representing content that can be translated into HTML or other
66
+ # publishable form.
67
+ def all_processable_children
68
+ all_files.select {|child| child.processable? and not child.hidden?}
69
+ end
70
+
71
+ # Returns an array of immediate children, files and folders.
72
+ def children
73
+ @children ||= get_children
74
+ end
75
+
76
+ # Add a new child object to the @children. Used by generators to add new files to the existing
77
+ # file-system-gathered pages. This method should only be called in a generator, since their
78
+ # execution is managed in such a way that new children are added at the right stage of
79
+ # processing.
80
+ #
81
+ # @param [WebsiteFile, WebsiteFolder] new_child The new generated object to add a child.
82
+ # @return nothing
83
+ def append_child(new_child)
84
+ # Ensure we get the children from disk first.
85
+ children
86
+ @children.push(new_child)
87
+ end
88
+
89
+ def get_children
90
+ tr = folder_contents.collect { |child_path| get_object_for_path(child_path) }
91
+ tr.reject {|c| c.nil?}
92
+ end
93
+
94
+ # Returns a WebsiteFile or WebsiteFolder for the given path.
95
+ #
96
+ # @param [Path] path The Path object representing the on-disk file or folder.
97
+ # @return [WebsiteFile, WebsiteFolder, nil] The child of this folder.
98
+ def get_object_for_path(path)
99
+ # TODO Add different file types: processable, resource, etc.
100
+
101
+ if path.file?
102
+ WebsiteFile.new(path, self)
103
+
104
+ elsif path.folder?
105
+ WebsiteFolder.new(path, self)
106
+
107
+ else
108
+ # ignore
109
+ nil
110
+ end
111
+ end
112
+
113
+ def folder_contents
114
+ @path.children
115
+ end
116
+
117
+ def get_child_by_path(path)
118
+ children.each do |child|
119
+ return child if child.path == path
120
+ end
121
+ nil
122
+ end
123
+
124
+ def get_child_by_rootname(rootname)
125
+ children.each do |child|
126
+ return child if child.path.rootname == rootname
127
+ end
128
+ nil
129
+ end
130
+
131
+ def get_child_by_basename(basename)
132
+ children.each do |child|
133
+ return child if child.path.basename == basename
134
+ end
135
+ nil
136
+ end
137
+
138
+ def get_child_by_filename(filename)
139
+ children.each do |child|
140
+ return child if child.path.filename == filename
141
+ end
142
+ nil
143
+ end
144
+
145
+ def is_immediate_child?(web_obj)
146
+ children.include?(web_obj)
147
+ end
148
+
149
+ def is_child?(web_obj)
150
+ folders_only = proc {|c| c.folder?}
151
+ tr = self.is_immediate_child?(web_obj)
152
+ return true if tr
153
+
154
+ v = Visitor.new(folders_only)
155
+ v.traverse(self) do |website_folder|
156
+ tr ||= website_folder.is_immediate_child?(web_obj)
157
+ end
158
+ tr
159
+ end
160
+
161
+ def has_index?
162
+ @has_index ||= get_has_index
163
+ end
164
+
165
+ def get_has_index
166
+ child_names = files.collect do |file|
167
+ filename = file.filename
168
+ ext = File.extname(filename)
169
+ filename.sub(ext, "")
170
+ end
171
+ child_names.include?("index")
172
+ end
173
+ end
174
+
175
+ end
176
+