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,86 @@
1
+ require 'core/registrar'
2
+
3
+ module Yuzu::Filters
4
+ include Yuzu::Registrar
5
+
6
+ # Filters are the primary means to derive information from a given source file and place new
7
+ # contents into it before it is placed in a layout.
8
+ #
9
+ # There are 3 stages of filtering that happen to each file.
10
+ #
11
+ # 1. prefilter -- replacing LINKROOT and CURRENTPATH, so the next phase has the proper paths
12
+ # 2. filter -- transforming contents
13
+ # 3. postfilter -- replacing LINKROOT and CURRENTPATH again
14
+ class Filter < Register
15
+ @@filters = {}
16
+ def self.registry
17
+ :filters
18
+ end
19
+ def self.filters
20
+ @@filters
21
+ end
22
+
23
+ attr_reader :name, :directive
24
+
25
+ def initialize
26
+ @name = :directive
27
+ @directive = "DIRECTIVE"
28
+ end
29
+
30
+ def filter_type
31
+ [:filter]
32
+ end
33
+
34
+ def default(website_file=nil)
35
+ nil
36
+ end
37
+
38
+ def value(website_file)
39
+ get_value(website_file) || default(website_file)
40
+ end
41
+
42
+ def get_value(website_file)
43
+ match(website_file.raw_contents)
44
+ end
45
+
46
+ def match(contents)
47
+ get_match(contents)
48
+ end
49
+
50
+ def get_match(contents)
51
+ m = contents.match(regex)
52
+ m.nil? ? nil : m[1]
53
+ end
54
+
55
+ # Returns the contents to replace the directive with as written.
56
+ #
57
+ # @param [WebsiteFile] website_file The page in which the directive appears
58
+ # @param [String, nil] processing_contents The contents of the given WebsiteFile as they are
59
+ # being transformed by processing.
60
+ # @return [String] What to replace the directive with.
61
+ def replacement(website_file, processing_contents=nil)
62
+ ""
63
+ end
64
+
65
+ def regex
66
+ Regexp.new('^\s*' + @directive.to_s + '\(([\w\s\.\,\'\"\/\-:]*?)\)')
67
+ end
68
+
69
+ def process(website_file, processing_contents)
70
+ m = processing_contents.match(regex)
71
+
72
+ while not m.nil?
73
+ repl = replacement(website_file, processing_contents)
74
+
75
+ # Remove the next match.
76
+ processing_contents = processing_contents.sub(regex, repl.to_s)
77
+
78
+ # Find any others...
79
+ m = processing_contents.match(regex)
80
+ end
81
+
82
+ processing_contents
83
+ end
84
+ end
85
+ end
86
+
@@ -0,0 +1,248 @@
1
+ require 'filters/base'
2
+ require 'core/template'
3
+ require 'helpers/path'
4
+ require 'html/base'
5
+
6
+
7
+ module Yuzu::Filters
8
+ include Yuzu::Core
9
+
10
+ class CatalogFilter < Filter
11
+ def initialize
12
+ @name = :catalog
13
+ @directive = "INSERTCATALOG"
14
+ end
15
+
16
+ def value(website_file)
17
+ nil
18
+ end
19
+
20
+ def replacement(website_file, processing_contents=nil)
21
+ catalog = Yuzu::Filters.catalog_for(website_file, match(processing_contents))
22
+
23
+ if catalog.should_paginate? and catalog.num_pages > 1
24
+ website_file.stash(:catalog => catalog)
25
+ end
26
+
27
+ return catalog.render
28
+ end
29
+ end
30
+ Filter.register(:catalog => CatalogFilter)
31
+
32
+
33
+ # Create a Catalog instance for a given match
34
+ #
35
+ # @param [WebsiteFile] website_file The file in which the catalog is being rendered.
36
+ # @param [String] match_string The match given by the filter, e.g. "page:1, per_row:3, ..."
37
+ # @return [Catalog] The catalog object responsible for rendering the pages specified.
38
+ def catalog_for(website_file, match_string)
39
+ kwds = Yuzu::Filters.get_kwds(match_string)
40
+ Catalog.new(kwds, website_file, match_string)
41
+ end
42
+ module_function :catalog_for
43
+
44
+ # Returns a Hash of the keyword arguments contained in the inner match string of the tag:
45
+ #
46
+ # INSERTCATALOG(page: 1, count:10)
47
+ #
48
+ # yields
49
+ #
50
+ # {:page => 1, :count => 10}
51
+ #
52
+ # @param [String] match_string The text inside the tag match.
53
+ # @return [Hash] The key to value pairs.
54
+ def get_kwds(match_string)
55
+ pairs = match_string.split(",").collect {|pair| pair.strip}
56
+
57
+ kwds = {}
58
+ pairs.each do |pair|
59
+ key, val = pair.split(":").collect {|el| el.strip}
60
+ kwds[key.to_sym] = val
61
+ end
62
+
63
+ kwds
64
+ end
65
+ module_function :get_kwds
66
+
67
+
68
+ # Catalogs render one page of collected contents and have no knowledge of pagination. The
69
+ # auto-pagination happens earlier as part of the generation process.
70
+ class Catalog
71
+ include Helpers
72
+ attr_reader :website_file
73
+
74
+ def initialize(kwds, website_file, original_match_string)
75
+ @kwds = kwds
76
+ @website_file = website_file
77
+ @original_match_string = original_match_string
78
+
79
+ @siteroot = website_file.root
80
+ end
81
+
82
+ # Define accessors for each of the given keyword arguments.
83
+ @@kwd_defaults = {
84
+ :template => '_block.haml',
85
+ :total => 1000,
86
+ :per_page => 10,
87
+ :per_row => 1,
88
+ :page => 1,
89
+ :offset => 0,
90
+ :category => nil
91
+ }
92
+ @@kwd_defaults.each_pair do |key, default_value|
93
+ define_method key do
94
+ if @kwds.has_key?(key)
95
+ # The user's arguments always come in as a String, so this converts the argument into its
96
+ # appropriate type as specified by the defaults Hash.
97
+ convert_method = get_default_type_method(key)
98
+ convert_method.nil? ? @kwds[key] : @kwds[key].send(convert_method)
99
+ else
100
+ default_value
101
+ end
102
+ end
103
+ end
104
+
105
+ def get_default_type_method(key)
106
+ klass = get_default_type(key)
107
+ if klass == String
108
+ :to_s
109
+ elsif klass == Fixnum
110
+ :to_i
111
+ elsif klass == Float
112
+ :to_f
113
+ else
114
+ nil
115
+ end
116
+ end
117
+
118
+ def get_default_type(key)
119
+ @@kwd_defaults.has_key?(key) ? @@kwd_defaults[key].class : nil
120
+ end
121
+
122
+ def should_paginate?
123
+ num_total_files > number_of_files_to_show
124
+ end
125
+
126
+ def num_total_files
127
+ [target_files.length, total].min
128
+ end
129
+
130
+ def num_pages
131
+ return 1 if not should_paginate?
132
+ (num_total_files.to_f / per_page.to_f).ceil
133
+ end
134
+
135
+ def original_directive
136
+ # TODO build a regex forgiving of whitespace
137
+ "INSERTCATALOG(#{@original_match_string})"
138
+ end
139
+
140
+ def directive_for_page(page)
141
+ with_page = @kwds.merge({:page => page})
142
+ args = with_page.collect {|key, val| "#{key}:#{val}"}.join(", ")
143
+ "INSERTCATALOG(#{args})"
144
+ end
145
+
146
+ def formatted_directive
147
+ args = @kwds.collect {|key, val| "#{key}:#{val}"}.join(", ")
148
+ "INSERTCATALOG(#{args})"
149
+ end
150
+
151
+ def render
152
+ if well_formed?
153
+ rows = []
154
+ num_rows_this_page.times do |row|
155
+ rows.push(render_nth_row(row))
156
+ end
157
+ rows.join("\n")
158
+ else
159
+ Html::Comment.new << "Error in processing the catalog. Got #{formatted_directive}"
160
+ end
161
+ end
162
+
163
+ def render_nth_row(row)
164
+ start = row * per_row
165
+ stop = (row + 1) * per_row
166
+ files_this_row = files_to_render_this_page[start...stop]
167
+ if files_this_row.empty?
168
+ Html::Div.new(:class => 'row') << Html::Comment.new << "No files in this row."
169
+ else
170
+ render_row(files_this_row)
171
+ end
172
+ end
173
+
174
+ def render_row(files)
175
+ rendered_files = files.collect {|file| render_post(file)}.join("\n")
176
+ Html::Div.new(:class => 'row') << rendered_files
177
+ end
178
+
179
+ def render_post(file)
180
+ partial.render(file, {:klass => css_class})
181
+ end
182
+
183
+ def css_class
184
+ num_columns_12 = (12.0 / per_row).floor
185
+ "span#{num_columns_12}"
186
+ end
187
+
188
+ def partial
189
+ Yuzu::Core::HamlTemplate.new(template)
190
+ end
191
+
192
+ def well_formed?
193
+ @kwds.has_key?(:path)
194
+ end
195
+
196
+ def target_folder
197
+ case
198
+ when (not well_formed?)
199
+ nil
200
+ when @kwds[:path] == ""
201
+ @siteroot
202
+ else
203
+ search_path = Path.new(@kwds[:path])
204
+ @siteroot.find_file_by_path(search_path)
205
+ end
206
+ end
207
+
208
+ def target_files
209
+ @target_files ||= get_target_files
210
+ end
211
+
212
+ def get_target_files
213
+ return [] if target_folder.nil?
214
+ unsorted = target_folder.all_processable_children.reject {|node| node.index?}
215
+ if not category.nil?
216
+ unsorted = unsorted.select {|file| file_has_category?(file)}
217
+ end
218
+ unsorted.sort {|a, b| b.post_date <=> a.post_date}
219
+ end
220
+
221
+ def file_has_category?(file)
222
+ file.categories.each do |cat|
223
+ if cat.name == category.downcase.dasherize
224
+ return true
225
+ end
226
+ end
227
+ return false
228
+ end
229
+
230
+ # Returns an array of WebsiteFile objects that are the contents of the catalog on this page.
231
+ def files_to_render_this_page
232
+ start = (page - 1) * per_page + offset
233
+ stop = page * per_page + offset
234
+ target_files[start...stop]
235
+ end
236
+
237
+ def num_rows_this_page
238
+ (number_of_files_to_show.to_f / per_row.to_f).ceil
239
+ end
240
+
241
+ def number_of_files_to_show
242
+ [total, per_page, target_files.length, page * per_page].min
243
+ end
244
+
245
+ end
246
+ end
247
+
248
+
@@ -0,0 +1,58 @@
1
+ require 'filters/base'
2
+ require 'helpers/url'
3
+ require 'helpers/path'
4
+
5
+ module Yuzu::Filters
6
+ class CategoriesFilter < Filter
7
+ def initialize
8
+ @name = :categories
9
+ @directive = "CATEGORIES"
10
+ end
11
+
12
+ def default(website_file)
13
+ [Category.new("uncategorized", website_file)]
14
+ end
15
+
16
+ def get_value(website_file)
17
+ m = match(website_file.raw_contents)
18
+ return default(website_file) if m.nil?
19
+
20
+ category_list = m.split(",")
21
+ category_list.collect! {|cat| cat.strip.downcase}
22
+ category_list.reject! {|cat| cat.empty?}
23
+ category_list.collect {|cat| Category.new(cat, website_file)}
24
+ end
25
+ end
26
+ Filter.register(:categories => CategoriesFilter)
27
+
28
+
29
+ class Category
30
+ attr_reader :name
31
+
32
+ def initialize(name, website_file)
33
+ @name = name.dasherize.downcase
34
+ @website_file = website_file
35
+ end
36
+
37
+ def link
38
+ Html::Link.new(:href => url) << @name.titlecase
39
+ end
40
+
41
+ def url
42
+ blog_folder.link_url + "/" + @name
43
+ end
44
+
45
+ def path
46
+ Helpers::Path.new(blog_folder.path.name, @name)
47
+ end
48
+
49
+ def blog_folder
50
+ @website_file.blog_folder
51
+ end
52
+
53
+ def <=>(other)
54
+ @name <=> other.name
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,35 @@
1
+ require 'filters/base'
2
+ require 'helpers/url'
3
+
4
+ module Yuzu::Filters
5
+ class CurrentpathFilter < Filter
6
+ include Helpers
7
+
8
+ def initialize
9
+ @name = :currentpath
10
+ @directive = "CURRENTPATH"
11
+ end
12
+
13
+ def filter_type
14
+ # Filter the LINKROOT in IMAGES and other path-dependent tags first, so they have the proper
15
+ # paths to search for. Then process LINKROOT afterwards.
16
+ [:prefilter, :postfilter]
17
+ end
18
+
19
+ def regex
20
+ /CURRENTPATH/
21
+ end
22
+
23
+ def get_value(website_file)
24
+ linkroot = website_file.config.linkroot
25
+ pathname = website_file.path.dirname
26
+ Url.new(pathname, prefix=linkroot)
27
+ end
28
+
29
+ def replacement(website_file, processing_contents=nil)
30
+ get_value(website_file)
31
+ end
32
+ end
33
+ Filter.register(:currentpath => CurrentpathFilter)
34
+ end
35
+
@@ -0,0 +1,16 @@
1
+ require 'filters/base'
2
+
3
+ module Yuzu::Filters
4
+ class DescriptionFilter < Filter
5
+ def initialize
6
+ @name = :description
7
+ @directive = "DESCRIPTION"
8
+ end
9
+
10
+ def default(website_file=nil)
11
+ ""
12
+ end
13
+ end
14
+ Filter.register(:description => DescriptionFilter)
15
+ end
16
+