henshin 0.4.2 → 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/LICENCE +18 -0
  2. data/README.md +133 -0
  3. data/Rakefile +6 -51
  4. data/bin/henshin +127 -134
  5. data/lib/henshin.rb +163 -38
  6. data/lib/henshin/compressor.rb +31 -0
  7. data/lib/henshin/compressors/css.rb +20 -0
  8. data/lib/henshin/compressors/js.rb +20 -0
  9. data/lib/henshin/core_ext.rb +69 -0
  10. data/lib/henshin/error.rb +28 -0
  11. data/lib/henshin/file.rb +67 -0
  12. data/lib/henshin/files/abstract.rb +102 -0
  13. data/lib/henshin/files/attributes.rb +31 -0
  14. data/lib/henshin/files/empty_template.rb +24 -0
  15. data/lib/henshin/files/physical.rb +117 -0
  16. data/lib/henshin/files/post.rb +64 -0
  17. data/lib/henshin/files/templatable.rb +29 -0
  18. data/lib/henshin/files/template.rb +45 -0
  19. data/lib/henshin/files/tilt.rb +50 -0
  20. data/lib/henshin/files/tilt_template.rb +45 -0
  21. data/lib/henshin/package.rb +27 -0
  22. data/lib/henshin/packages/script.rb +23 -0
  23. data/lib/henshin/packages/style.rb +20 -0
  24. data/lib/henshin/path.rb +143 -0
  25. data/lib/henshin/publisher.rb +42 -0
  26. data/lib/henshin/publishers/sftp.rb +79 -0
  27. data/lib/henshin/reader.rb +68 -0
  28. data/lib/henshin/safety.rb +74 -0
  29. data/lib/henshin/scope.rb +55 -0
  30. data/lib/henshin/site.rb +291 -228
  31. data/lib/henshin/ui.rb +35 -0
  32. data/lib/henshin/version.rb +3 -0
  33. data/lib/henshin/writer.rb +43 -0
  34. data/lib/rack/henshin.rb +113 -0
  35. data/site/assets/scripts/config.js +11 -0
  36. data/site/assets/styles/_mixins.sass +16 -0
  37. data/site/assets/styles/screen.sass +198 -0
  38. data/site/config.yml +13 -0
  39. data/site/drafts/a-work-in-progress.md +5 -0
  40. data/site/feed.xml.slim +19 -0
  41. data/site/index.html.slim +28 -0
  42. data/site/init.rb +33 -0
  43. data/site/posts/1-hello-world.md +46 -0
  44. data/site/posts/2-code-testing.md +36 -0
  45. data/site/posts/3-style-test.md +80 -0
  46. data/site/templates/default.slim +11 -0
  47. data/site/templates/post.slim +30 -0
  48. data/site/test.html.md +7 -0
  49. data/spec/helper.rb +70 -0
  50. data/spec/henshin/compressor_spec.rb +25 -0
  51. data/spec/henshin/compressors/css_spec.rb +22 -0
  52. data/spec/henshin/compressors/js_spec.rb +22 -0
  53. data/spec/henshin/core_ext_spec.rb +59 -0
  54. data/spec/henshin/error_spec.rb +22 -0
  55. data/spec/henshin/file_spec.rb +28 -0
  56. data/spec/henshin/files/abstract_spec.rb +98 -0
  57. data/spec/henshin/files/attributes_spec.rb +25 -0
  58. data/spec/henshin/files/empty_template_spec.rb +11 -0
  59. data/spec/henshin/files/physical_spec.rb +55 -0
  60. data/spec/henshin/files/post_spec.rb +66 -0
  61. data/spec/henshin/files/template_spec.rb +53 -0
  62. data/spec/henshin/files/tilt_spec.rb +59 -0
  63. data/spec/henshin/package_spec.rb +24 -0
  64. data/spec/henshin/packages/script_spec.rb +17 -0
  65. data/spec/henshin/packages/style_spec.rb +17 -0
  66. data/spec/henshin/path_spec.rb +56 -0
  67. data/spec/henshin/publisher_spec.rb +44 -0
  68. data/spec/henshin/publishers/sftp_spec.rb +21 -0
  69. data/spec/henshin/reader_spec.rb +55 -0
  70. data/spec/henshin/safety_spec.rb +66 -0
  71. data/spec/henshin/scope_spec.rb +33 -0
  72. data/spec/henshin/site_spec.rb +142 -0
  73. data/spec/henshin/ui_spec.rb +26 -0
  74. data/spec/henshin/writer_spec.rb +26 -0
  75. metadata +352 -197
  76. data/.gitignore +0 -27
  77. data/LICENSE +0 -20
  78. data/README.markdown +0 -35
  79. data/VERSION +0 -1
  80. data/henshin.gemspec +0 -121
  81. data/lib/henshin/archive.rb +0 -133
  82. data/lib/henshin/exec/files.rb +0 -46
  83. data/lib/henshin/ext.rb +0 -55
  84. data/lib/henshin/gen.rb +0 -154
  85. data/lib/henshin/labels.rb +0 -144
  86. data/lib/henshin/plugin.rb +0 -100
  87. data/lib/henshin/plugins/highlight.rb +0 -24
  88. data/lib/henshin/plugins/liquid.rb +0 -61
  89. data/lib/henshin/plugins/maruku.rb +0 -18
  90. data/lib/henshin/plugins/sass.rb +0 -24
  91. data/lib/henshin/plugins/textile.rb +0 -18
  92. data/lib/henshin/post.rb +0 -156
  93. data/lib/henshin/static.rb +0 -33
  94. data/test/helper.rb +0 -44
  95. data/test/site/css/_reset.sass +0 -34
  96. data/test/site/css/print.css +0 -12
  97. data/test/site/css/screen.sass +0 -70
  98. data/test/site/includes/head.html +0 -1
  99. data/test/site/index.html +0 -23
  100. data/test/site/layouts/archive_date.html +0 -20
  101. data/test/site/layouts/archive_month.html +0 -24
  102. data/test/site/layouts/archive_year.html +0 -26
  103. data/test/site/layouts/category_index.html +0 -27
  104. data/test/site/layouts/category_page.html +0 -20
  105. data/test/site/layouts/main.html +0 -13
  106. data/test/site/layouts/post.html +0 -36
  107. data/test/site/layouts/tag_index.html +0 -27
  108. data/test/site/layouts/tag_page.html +0 -20
  109. data/test/site/options.yaml +0 -17
  110. data/test/site/plugins/test.rb +0 -3
  111. data/test/site/posts/Testing-Stuff.markdown +0 -14
  112. data/test/site/posts/Textile-Test.textile +0 -7
  113. data/test/site/posts/cat/test.markdown +0 -6
  114. data/test/site/posts/lorem-ipsum.markdown +0 -7
  115. data/test/site/posts/same-date.markdown +0 -7
  116. data/test/site/static.html +0 -19
  117. data/test/suite.rb +0 -4
  118. data/test/test_gen.rb +0 -98
  119. data/test/test_options.rb +0 -73
  120. data/test/test_post.rb +0 -67
  121. data/test/test_site.rb +0 -197
  122. data/test/test_static.rb +0 -13
@@ -0,0 +1,74 @@
1
+ module Henshin
2
+
3
+ # Allows a class to mark methods as "unsafe", using {.unsafe}. A copy of an
4
+ # object can then ask for a "safe" version with these methods disabled using
5
+ # {#safe}.
6
+ module Safety
7
+
8
+ module ClassMethods
9
+
10
+ # Mark a list of methods as "unsafe".
11
+ #
12
+ # @param syms [Symbol]
13
+ # @example
14
+ #
15
+ # unsafe :write
16
+ #
17
+ def unsafe(*syms)
18
+ @unsafe_methods += syms
19
+ end
20
+
21
+ # Makes sure that the Safety module, and any unsafe methods that have been
22
+ # marked, infect any class which inherits the original class.
23
+ def inherited(klass)
24
+ super
25
+ klass.send :include, ::Henshin::Safety
26
+ klass.instance_variable_set(:@unsafe_methods, @unsafe_methods)
27
+ end
28
+ end
29
+
30
+ # Mixes {ClassMethods} into the including class.
31
+ def self.included(base)
32
+ super
33
+ base.extend ClassMethods
34
+ base.instance_variable_set(:@unsafe_methods, [])
35
+ end
36
+
37
+ # @return [Array<Symbol>] List of methods marked unsafe.
38
+ def unsafe_methods
39
+ self.class.instance_variable_get(:@unsafe_methods)
40
+ end
41
+
42
+ def safe?
43
+ false
44
+ end
45
+
46
+ # @return A cloned version of the object with unsafe methods stubbed to
47
+ # return +nil+.
48
+ #
49
+ # @example
50
+ #
51
+ # class Test
52
+ # include Safety
53
+ #
54
+ # def unsafe_method; puts "Hello"; end
55
+ # unsafe :unsafe_method
56
+ # end
57
+ #
58
+ # Test.new.unsafe_method #=> "Hello"
59
+ # Test.new.safe.unsafe_method #=> nil
60
+ #
61
+ def safe
62
+ return @safe_clone if @safe_clone
63
+
64
+ @safe_clone = self.clone
65
+ unsafe_methods.each do |sym|
66
+ @safe_clone.singleton_class.send(:define_method, sym) {|*| nil }
67
+ end
68
+ @safe_clone.singleton_class.send(:define_method, :safe?) { true }
69
+
70
+ @safe_clone
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,55 @@
1
+ module Henshin
2
+
3
+ # Add methods here to make them available in templates.
4
+ module Helpers
5
+ def url_for(path)
6
+ file = @site.all_files.find {|i| i.path === path }
7
+ if file
8
+ file.url
9
+ else
10
+ path
11
+ end
12
+ end
13
+ end
14
+
15
+ # Creates an object that can be passed to a template from a Hash of data. Each
16
+ # key becomes a method that returns it's value, this is recursive and works
17
+ # over Arrays.
18
+ #
19
+ # @example
20
+ #
21
+ # data = {a: 1, b: 2}
22
+ # scope = Scope.new(data)
23
+ #
24
+ # scope.a #=> 1
25
+ # scope.b #=> 2
26
+ # scope.c #=> 3
27
+ #
28
+ class Scope
29
+
30
+ # @param data [Hash]
31
+ def initialize(data)
32
+ meta = (class << self; self; end)
33
+
34
+ data.each do |name, val|
35
+ case val
36
+ when ::Hash
37
+ meta.send(:define_method, name) { Scope.new(val) }
38
+ when ::Array
39
+ meta.send(:define_method, name) {
40
+ val.map {|i|
41
+ i.is_a?(::Hash) ? Scope.new(i) : i
42
+ }
43
+ }
44
+ else
45
+ meta.send(:define_method, name) { val }
46
+ end
47
+ end
48
+ end
49
+
50
+ def method_missing(sym, *args)
51
+ nil
52
+ end
53
+ end
54
+
55
+ end
@@ -1,249 +1,312 @@
1
1
  module Henshin
2
2
 
3
+ DEFAULT_TEMPLATE = 'default'
4
+
5
+ # The Site represents a site in the filesystem, it is in charge of reading and
6
+ # writing files.
3
7
  class Site
4
-
5
- # @return [Hash] the configuration made up of the override, options.yaml and Henshin::Defaults
6
- attr_accessor :config
7
-
8
- # @return [Pathname] the path to the site to generate from where the command is executed
9
- attr_accessor :root
10
-
11
- # @return [Pathname] the path to where the finished site should be written
12
- attr_accessor :target
13
-
14
- # @return [String] a path which should be prepended to all urls
15
- attr_accessor :base
16
-
17
- attr_accessor :gens, :posts, :statics, :layouts
18
- attr_accessor :tags, :categories, :archive
19
- attr_accessor :plugins
20
-
21
- # A new instance of site
22
- #
23
- # @param [Hash{String => Object}] override data to override loaded options
24
- # @return [self]
25
- def initialize(override={})
26
- self.reset
27
- self.configure(override)
28
- self.load_plugins
29
- self
30
- end
31
-
32
- # Resets all instance variables, except +config+ and +plugins+ as these shouldn't
33
- # need to be reloaded each time.
34
- #
35
- # @return [self]
36
- def reset
37
- @gens = []
38
- @posts = []
39
- @statics = []
40
- @layouts = {}
41
-
42
- @archive = Archive.new(self)
43
- @tags = Labels.new('tag', self)
44
- @categories = Labels.new('category', self)
45
- self
46
- end
47
-
48
- # Creates the configuration hash by merging defaults, supplied options and options
49
- # read from the 'options.yaml' file. Then sets root, target, base and appends
50
- # special directories to @config['exclude']
51
- #
52
- # @param [Hash] override to override other set options
53
- def configure(override)
54
- @config = {}
55
- config_file = File.join((override['root'] || Defaults['root']), '/options.yaml')
56
-
57
- # change target to represent given root, only if root given
58
- if override['root'] && !override['target']
59
- override['target'] = File.join(override['root'], Defaults['target'])
60
- end
61
-
62
- begin
63
- config = YAML.load_file(config_file)
64
- @config = Defaults.merge(config).merge(override)
65
- rescue => e
66
- $stderr.puts "\nCould not read configuration, falling back to defaults..."
67
- $stderr.puts "-> #{e.to_s}"
68
- @config = Defaults.merge(override)
8
+
9
+ include Safety
10
+
11
+ # Error for when a path given to {Site#initialize} does not contain a
12
+ # config.yml file.
13
+ class NotSiteError < StandardError
14
+
15
+ # @param place [Pathname, String] Path where site is expected
16
+ def initialize(place)
17
+ @place = place
69
18
  end
70
- @root = @config['root'].to_p
71
- @target = @config['target'].to_p
72
-
73
- @base = @config['base'] || "/"
74
- @base = '/' + @base unless @base[0] == '/' # need to make sure it starts with slash
75
-
76
- @config['exclude'] << '/_site' << '/plugins'
77
- end
78
-
79
- # Requires each plugin in @config['plugins'], then loads and sorts them into
80
- # +plugins+ by type
81
- def load_plugins
82
- @plugins = {:generators => {}, :layoutors => []}
83
-
84
- @config['plugins'].each do |plugin|
85
- begin
86
- require File.join('henshin', 'plugins', plugin)
87
- rescue LoadError
88
- require File.join(@root, 'plugins', plugin)
89
- end
19
+
20
+ def message
21
+ <<EOS
22
+ '#{@place}' does not contain a henshin site. No config.yml can be found.
23
+ Create one using `henshin new`, or find out more by reading `henshin help`.
24
+ EOS
90
25
  end
91
-
92
- Henshin::Generator.subclasses.each do |plugin|
93
- plugin = plugin.new(self)
94
- plugin.extensions[:input].each do |ext|
95
- @plugins[:generators][ext] = plugin
96
- end
26
+
27
+ # @return Empty array as backtrace is unnecessary.
28
+ def backtrace
29
+ []
97
30
  end
98
-
99
- @plugins[:layoutors] = Henshin::Layoutor.subclasses.map {|l| l.new(self)}.sort
100
- end
101
-
102
- ##
103
- # Read, process, render and write
104
- def build
105
- self.reset
106
- self.read
107
- self.process
108
- self.render
109
- self.write
110
- end
111
-
112
-
113
- ##
114
- # Reads all necessary files and puts them into the necessary arrays
115
- #
116
- # @return [self]
117
- def read
118
- self.read_layouts
119
- self.read_posts
120
- self.read_gens
121
- self.read_statics
122
- self
123
31
  end
124
-
125
- # Adds all items in '/layouts' to the layouts array
126
- def read_layouts
127
- path = File.join(@root, 'layouts')
128
- Dir.glob(path + '/*.*').each do |layout|
129
- layout =~ /([a-zA-Z0-9 _-]+)\.([a-zA-Z0-9-]+)/
130
- @layouts[$1] = File.open(layout, 'r') {|f| f.read}
131
- end
32
+
33
+ class_attr_accessor :files_list, :file_list, :ignore_list, :default => []
34
+
35
+ # Adds the methods specified to the file list. These methods must returns
36
+ # a single File object (or subclass). This adds the File to those returned
37
+ # by {#all_files}, and so they will be written and served properly.
38
+ #
39
+ # @param names [Symbol]
40
+ # @see .files
41
+ # @example
42
+ #
43
+ # class IndexFile < File::Abstract
44
+ # # ...
45
+ # end
46
+ #
47
+ # class IndexedSite < Site
48
+ # def index
49
+ # IndexFile.new(self)
50
+ # end
51
+ #
52
+ # file :index
53
+ # end
54
+ #
55
+ def self.file(*names)
56
+ names.each {|n| file_list << n }
132
57
  end
133
-
134
- # Adds all items in '/posts' to the posts array
135
- def read_posts
136
- path = File.join(@root, 'posts')
137
- Dir.glob(path + '/**/*.*').each do |post|
138
- @posts << Post.new(post.to_p, self).read
139
- end
58
+
59
+ # Adds the methods specified to the files list. These methods must return
60
+ # a list of File objects (or subclasses). This adds the Files to those
61
+ # returned by {#all_files}, and so they will be written and served
62
+ # properly.
63
+ #
64
+ # @param names [Symbol]
65
+ # @see .file
66
+ # @example
67
+ #
68
+ # class Recipe < File::Physical
69
+ # # ...
70
+ # end
71
+ #
72
+ # class RecipeSite < Site
73
+ # def recipes
74
+ # @reader.read_all('recipes').map do |path|
75
+ # Recipe.new(self, path)
76
+ # end
77
+ # end
78
+ #
79
+ # files :recipes
80
+ # end
81
+ #
82
+ def self.files(*names)
83
+ names.each {|n| files_list << n }
140
84
  end
141
-
142
- # Adds all files that need to be run through a plugin in an array
143
- def read_gens
144
- files = Dir.glob( File.join(@root, '**', '*.*') )
145
- gens = files.select {|i| gen?(i) }
146
- gens.each do |gen|
147
- @gens << Gen.new(gen.to_p, self).read
148
- end
85
+
86
+ # Makes this site ignore the paths given. This has the same effect as
87
+ # setting the ignore option in a site's +config.yml+, but has effect for any
88
+ # site using this specific class, regardless of the config.
89
+ #
90
+ # @param paths [String]
91
+ # @example
92
+ #
93
+ # class DataSite
94
+ # ignore 'data'
95
+ # # ...
96
+ # end
97
+ #
98
+ def self.ignore(*paths)
99
+ paths.each {|p| ignore_list << p }
149
100
  end
150
-
151
- # Adds all static files to an array
152
- def read_statics
153
- files = Dir.glob( File.join(@root, '**', '*.*') )
154
- static = files.select {|i| static?(i) }
155
- static.each do |static|
156
- @statics << Static.new(static.to_p, self)
101
+
102
+ # Creates a new instance of Site.
103
+ #
104
+ # @param root [String, Pathname]
105
+ # Path to where the site is located.
106
+ #
107
+ # @raise [NotSiteError]
108
+ # Raised if +root+ does not contain a +config.yml+ file.
109
+ #
110
+ def initialize(root='.')
111
+ @reader = Reader.new(root)
112
+ @source = Pathname.new(root)
113
+
114
+ unless (@source + 'config.yml').exist?
115
+ raise NotSiteError, @source
116
+ end
117
+
118
+ @reader.ignore *ignore_list
119
+ if config[:ignore]
120
+ @reader.ignore *config[:ignore]
157
121
  end
158
122
  end
159
123
 
160
-
161
- ##
162
- # Processes all of the necessary files
163
- def process
164
- @posts.sort!
165
- @gens.sort!
166
-
167
- @posts.each do |post|
168
- @tags << post if post.data['tags']
169
- @categories << post if post.data['category']
170
- @archive << post
124
+ # @return [Pathname] Path of site being read
125
+ attr_reader :source
126
+
127
+ # Destination folder to build into. Uses destination set in config if
128
+ # available which can be either a relative path or absolute. By default uses
129
+ # 'build'.
130
+ #
131
+ # @return [Pathname] Path to build site to
132
+ def dest
133
+ @source.expand_path + (config[:dest] || 'build')
134
+ end
135
+
136
+ # Root url, this is guaranteed to begin and end with a forward-slash. All
137
+ # urls and permalinks should begin with this. Uses +:root+ option from
138
+ # config if set, otherwise defaults to using +/+.
139
+ #
140
+ # @return [Pathname] Root url
141
+ def root
142
+ u = config[:root] || '/'
143
+ u = '/' + u if u[0] != '/'
144
+ u = u + '/' if u[-1] != '/'
145
+ Pathname.new(u)
146
+ end
147
+
148
+ # @return [Hash] Defaults to for the Site.
149
+ def defaults
150
+ {
151
+ permalink: '/:title/index.html',
152
+ sass: {
153
+ load_paths: [@source + 'assets' + 'styles']
154
+ },
155
+ md: {
156
+ no_intra_emphasis: true,
157
+ fenced_code_blocks: true,
158
+ strikethrough: true,
159
+ superscript: true
160
+ },
161
+ compress: {
162
+ scripts: true,
163
+ styles: true,
164
+ images: true
165
+ }
166
+ }
167
+ end
168
+
169
+ private :defaults
170
+
171
+ # @return [Hashie::Mash] Loaded contents of +config.yml+.
172
+ def yaml
173
+ Hashie::Mash.new Henshin.load_yaml (@source + 'config.yml').read
174
+ end
175
+
176
+ private :yaml
177
+
178
+ # @return [Hashie::Mash] Returns the configuration for the Site object, this
179
+ # is the combination of the options loaded from +config.yml+ and the
180
+ # defaults.
181
+ def config
182
+ Hashie::Mash.new defaults.merge(yaml)
183
+ end
184
+
185
+ # @return [Package::Script] The script package file. This is a combined and
186
+ # minified file containing the contents of the files in +assets/scripts+.
187
+ def script
188
+ Package::Script.new self, @reader.read_all('assets', 'scripts')
189
+ end
190
+
191
+ # @return [Package::Style] The style package file. This is a combined and
192
+ # minified file containing the contents of the files in +assets/styles+.
193
+ def style
194
+ Package::Style.new self, @reader.read_all('assets', 'styles')
195
+ end
196
+
197
+ file :script, :style
198
+
199
+ # @return [Array<File::Post>] The Posts read from the +posts+ folder. These
200
+ # are sorted and all posts have had the next and previous posts set
201
+ # correctly.
202
+ def posts
203
+ weave_posts read(:all, 'posts')
204
+ end
205
+
206
+ # @return [Array<File>] Returns all other files, those which have no special
207
+ # meaning to Henshin.
208
+ def files
209
+ read :safe_paths
210
+ end
211
+
212
+ files :posts, :files
213
+
214
+ # @return [Array<File>] Returns a list of all files set using {.files} and
215
+ # {.file}. These are the files which should be written, served, etc.
216
+ def all_files
217
+ files_list.map {|fs| send(fs) }.reduce(:+) + file_list.map {|f| send(f) }
218
+ end
219
+
220
+ # @return [Array<File::Template>] Returns a list of all template files read from
221
+ # the +templates+ directory.
222
+ def templates
223
+ read :all, 'templates'
224
+ end
225
+
226
+ # Reads the paths using the method specified. It returns not a list of
227
+ # Pathname objects but the correct File objects, using {File.create}.
228
+ #
229
+ # @param sym [Symbol] Method to use for reading.
230
+ # @param args Any arguments to pass along with +sym+.
231
+ def read(sym, *args)
232
+ @reader.send(sym, *args).map {|p| File.create(self, p) }.sort
233
+ end
234
+
235
+ private :read
236
+
237
+ # For the given posts sets the correct previous and next posts.
238
+ #
239
+ # @param posts [Array<#next=,#prev=>] List of Post objects, must be sorted.
240
+ def weave_posts(posts)
241
+ posts.each_index do |i|
242
+ if i < posts.length
243
+ posts[i].prev = posts[i+1]
244
+ end
245
+
246
+ if i > 0
247
+ posts[i].next = posts[i-1]
248
+ end
171
249
  end
172
- self
250
+
251
+ posts
173
252
  end
174
-
175
- # @return [Hash] the payload for the layout engine
176
- def payload
177
- r = {'site' => @config}
178
- r['site']['created_at'] = Time.now
179
- r['site']['posts'] = @posts.collect{|i| i.to_hash}
180
- r['site']['tags'] = @tags.to_hash
181
- r['site']['categories'] = @categories.to_hash
182
- r['site']['archive'] = @archive.to_hash
183
- r
184
- end
185
-
186
- ##
187
- # Renders the files
188
- def render
189
- @posts.each {|post| post.render}
190
- @gens.each {|gen| gen.render}
191
-
192
- self
253
+
254
+ private :weave_posts
255
+
256
+ # If +sym+ is a key in the loaded +config.yml+ file, the value is returned,
257
+ # otherwise returns +nil+. This is so the Site object itself can be passed
258
+ # to templates.
259
+ def method_missing(sym, *args, &block)
260
+ if yaml.key?(sym)
261
+ yaml[sym]
262
+ else
263
+ nil
264
+ end
193
265
  end
194
-
195
-
196
- ##
197
- # Writes the files
198
- def write
199
- @posts.each {|post| post.write}
200
- @gens.each {|gen| gen.write}
201
- @statics.each {|static| static.write}
202
-
203
- @archive.write
204
- @tags.write
205
- @categories.write
266
+
267
+ # Writes the site using the given +writer+.
268
+ #
269
+ # @param writer [#write]
270
+ # @example
271
+ #
272
+ # class MyWriter
273
+ # def write(path, contents)
274
+ # # ...
275
+ # end
276
+ # end
277
+ #
278
+ # site.write MyWriter.new
279
+ #
280
+ def write(writer)
281
+ all_files.each do |file|
282
+ file.write writer
283
+ end
206
284
  self
207
285
  end
208
-
209
-
210
- # @param [String] path to test
211
- # @return [Bool] whether the path points to a static
212
- def static?( path )
213
- !( layout?(path) || post?(path) || gen?(path) || ignored?(path) )
214
- end
215
-
216
- # @param [String] path to test
217
- # @return [Bool] whether the path points to a layout
218
- def layout?( path )
219
- path.include?('layouts/') && !ignored?(path)
220
- end
221
-
222
- # @param [String] path to test
223
- # @return [Bool] whether the path points to a post
224
- def post?( path )
225
- path.include?('posts/') && !ignored?(path)
226
- end
227
-
228
- # @param [String] path to test
229
- # @return [Bool] whether the path points to a gen
230
- def gen?( path )
231
- return false if post?(path) || layout?(path) || ignored?(path)
232
- return true if @plugins[:generators].has_key? path.to_p.extname[1..-1]
233
- return true if File.open(path, "r").read(3) == "---"
234
- false
235
- end
236
-
237
- # @param [String] path to test
238
- # @return [Bool] whether the path points to a file which should be ignored
239
- def ignored?( path )
240
- ignored = ['/options.yaml'] + @config['exclude']
241
- ignored.collect! {|i| File.join(@root, i)}
242
- ignored.each do |i|
243
- return true if path.include? i
286
+
287
+ unsafe :write
288
+
289
+ # Finds the template with the name given. Given a list of names it tries to
290
+ # find a template for a single name working down the list, this allows
291
+ # "fallback" options to be given. If none are found it returns an instance
292
+ # of the {File::EmptyTemplate}.
293
+ #
294
+ # @param names [String]
295
+ # @return [File::Template, File::EmptyTemplate]
296
+ # @example
297
+ #
298
+ # site.template('recipe', Henshin::DEFAULT_TEMPLATE)
299
+ # #=> #<Henshin::File::Template ...>
300
+ #
301
+ def template(*names)
302
+ names.each do |name|
303
+ if tmp = templates.find {|t| t.name == name }
304
+ return tmp
305
+ end
244
306
  end
245
- false
307
+
308
+ File::EmptyTemplate.new
246
309
  end
247
-
310
+
248
311
  end
249
- end
312
+ end