yuzu 0.2.1.pre

Sign up to get free protection for your applications and to get access to all the features.
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
+