ruhoh 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/Gemfile +13 -0
  2. data/README.md +5 -53
  3. data/Rakefile +56 -0
  4. data/bin/ruhoh +23 -14
  5. data/lib/ruhoh/client/client.rb +310 -0
  6. data/lib/ruhoh/client/help.yml +56 -0
  7. data/lib/ruhoh/compiler.rb +16 -19
  8. data/lib/ruhoh/converters/converter.rb +30 -3
  9. data/lib/ruhoh/db.rb +7 -17
  10. data/lib/ruhoh/deployers/s3.rb +71 -0
  11. data/lib/ruhoh/friend.rb +71 -0
  12. data/lib/ruhoh/page.rb +41 -11
  13. data/lib/ruhoh/parsers/drafts.rb +54 -0
  14. data/lib/ruhoh/parsers/layouts.rb +14 -9
  15. data/lib/ruhoh/parsers/pages.rb +47 -35
  16. data/lib/ruhoh/parsers/partials.rb +14 -2
  17. data/lib/ruhoh/parsers/posts.rb +139 -88
  18. data/lib/ruhoh/parsers/routes.rb +4 -8
  19. data/lib/ruhoh/parsers/site.rb +2 -8
  20. data/lib/ruhoh/previewer.rb +48 -0
  21. data/lib/ruhoh/templaters/base.rb +53 -0
  22. data/lib/ruhoh/templaters/helpers.rb +159 -0
  23. data/lib/ruhoh/templaters/rmustache.rb +29 -0
  24. data/lib/ruhoh/utils.rb +25 -7
  25. data/lib/ruhoh/version.rb +1 -1
  26. data/lib/ruhoh/watch.rb +22 -9
  27. data/lib/ruhoh.rb +74 -29
  28. data/ruhoh.gemspec +70 -9
  29. data/scaffolds/blog/_config.yml +33 -0
  30. data/scaffolds/blog/_drafts/.gitkeep +0 -0
  31. data/scaffolds/blog/_posts/.gitkeep +0 -0
  32. data/scaffolds/blog/_site.yml +16 -0
  33. data/scaffolds/blog/_templates/partials/categories_list +3 -0
  34. data/scaffolds/blog/_templates/partials/pages_list +7 -0
  35. data/scaffolds/blog/_templates/partials/posts_collate +9 -0
  36. data/scaffolds/blog/_templates/partials/posts_list +1 -0
  37. data/scaffolds/blog/_templates/partials/tags_list +3 -0
  38. data/scaffolds/blog/_templates/syntax/google_prettify/default.css +52 -0
  39. data/scaffolds/blog/_templates/syntax/google_prettify/desert.css +34 -0
  40. data/scaffolds/blog/_templates/syntax/google_prettify/sons-of-obsidian.css +117 -0
  41. data/scaffolds/blog/_templates/syntax/google_prettify/sunburst.css +51 -0
  42. data/scaffolds/blog/_templates/syntax/google_prettify/twitter-bootstrap.css +30 -0
  43. data/scaffolds/blog/_templates/themes/twitter/bootstrap/css/bootstrap.min.css +689 -0
  44. data/scaffolds/blog/_templates/themes/twitter/bootstrap/img/glyphicons-halflings-white.png +0 -0
  45. data/scaffolds/blog/_templates/themes/twitter/bootstrap/img/glyphicons-halflings.png +0 -0
  46. data/scaffolds/blog/_templates/themes/twitter/css/style.css +68 -0
  47. data/scaffolds/blog/_templates/themes/twitter/layouts/default.html +64 -0
  48. data/scaffolds/blog/_templates/themes/twitter/layouts/page.html +13 -0
  49. data/scaffolds/blog/_templates/themes/twitter/layouts/post.html +55 -0
  50. data/scaffolds/blog/_templates/themes/twitter/manifest.yml +11 -0
  51. data/scaffolds/blog/about.md +5 -0
  52. data/scaffolds/blog/archive.html +11 -0
  53. data/scaffolds/blog/categories.html +21 -0
  54. data/scaffolds/blog/config.ru +9 -0
  55. data/scaffolds/blog/index.html +13 -0
  56. data/scaffolds/blog/pages.html +14 -0
  57. data/scaffolds/blog/tags.html +21 -0
  58. data/scaffolds/layout.html +3 -0
  59. data/scaffolds/page.html +6 -0
  60. data/scaffolds/post.html +8 -0
  61. data/scaffolds/theme/css/style.css +0 -0
  62. data/scaffolds/theme/images/.gitkeep +0 -0
  63. data/scaffolds/theme/layouts/default.html +17 -0
  64. data/scaffolds/theme/layouts/page.html +7 -0
  65. data/scaffolds/theme/layouts/post.html +8 -0
  66. data/scaffolds/theme/partials/.gitkeep +0 -0
  67. data/spec/db_spec.rb +88 -0
  68. data/spec/page_spec.rb +200 -0
  69. data/spec/parsers/layouts_spec.rb +32 -0
  70. data/spec/parsers/pages_spec.rb +97 -0
  71. data/spec/parsers/posts_spec.rb +301 -0
  72. data/spec/parsers/routes_spec.rb +45 -0
  73. data/spec/parsers/site_spec.rb +32 -0
  74. data/spec/setup_spec.rb +72 -0
  75. data/spec/spec_helper.rb +23 -0
  76. data/system_partials/analytics/getclicky +12 -0
  77. data/system_partials/analytics/google +11 -0
  78. data/system_partials/comments/disqus +13 -0
  79. data/system_partials/comments/facebook +9 -0
  80. data/system_partials/comments/intensedebate +6 -0
  81. data/system_partials/comments/livefyre +6 -0
  82. data/system_partials/syntax/google_prettify +11 -0
  83. metadata +84 -23
  84. data/lib/ruhoh/client.rb +0 -28
  85. data/lib/ruhoh/preview.rb +0 -36
  86. data/lib/ruhoh/templaters/helper_mustache.rb +0 -109
  87. data/lib/ruhoh/templaters/templater.rb +0 -39
data/lib/ruhoh/db.rb CHANGED
@@ -3,24 +3,12 @@ require "observer"
3
3
  class Ruhoh
4
4
 
5
5
  # Public: Database class for interacting with "data" in Ruhoh.
6
- #
7
6
  class DB
8
-
9
7
  class << self
10
8
  include Observable
11
- attr_reader :site, :routes, :posts, :pages, :layouts, :partials
9
+ WhiteList = [:site, :posts, :drafts, :pages, :routes, :layouts, :partials]
10
+ self.__send__ :attr_reader, *WhiteList
12
11
 
13
- # Note this is class-level so you have to call it manually.
14
- def initialize
15
- @site = ''
16
- @routes = ''
17
- @posts = ''
18
- @pages = ''
19
- @layouts = ''
20
- @partials = ''
21
- self.update!
22
- end
23
-
24
12
  def update(name)
25
13
  self.instance_variable_set("@#{name}",
26
14
  case name
@@ -30,6 +18,8 @@ class Ruhoh
30
18
  Ruhoh::Parsers::Routes.generate
31
19
  when :posts
32
20
  Ruhoh::Parsers::Posts.generate
21
+ when :drafts
22
+ Ruhoh::Parsers::Drafts.generate
33
23
  when :pages
34
24
  Ruhoh::Parsers::Pages.generate
35
25
  when :layouts
@@ -45,9 +35,9 @@ class Ruhoh
45
35
  end
46
36
 
47
37
  def update!
48
- self.instance_variables.each { |var|
49
- self.__send__ :update, var.to_s.gsub('@', '').to_sym
50
- }
38
+ WhiteList.each do |var|
39
+ self.__send__ :update, var
40
+ end
51
41
  end
52
42
 
53
43
  end #self
@@ -0,0 +1,71 @@
1
+ require 'aws/s3'
2
+
3
+ class Ruhoh
4
+
5
+ module Deployers
6
+
7
+ # Deploy to Amazon S3
8
+ # See http://amazon.rubyforge.org/ for usage.
9
+ class AmazonS3 < AWS::S3::S3Object
10
+
11
+ def initialize
12
+ credentials = Ruhoh::Utils.parse_file_as_yaml(Ruhoh.paths.site_source, "_deploy.yml")['s3']
13
+ self.connect(credentials)
14
+ self.ensure_bucket(credentials["bucket"])
15
+ #set_current_bucket_to(credentials["bucket"])
16
+ @bucket = credentials["bucket"]
17
+ end
18
+
19
+ def connect(credentials)
20
+ AWS::S3::Base.establish_connection!({
21
+ :access_key_id => credentials["access_key_id"],
22
+ :secret_access_key => credentials["secret_access_key"]
23
+ })
24
+ end
25
+
26
+ def deploy(compiled_directory)
27
+ FileUtils.cd(compiled_directory) {
28
+ Dir.glob("**/**") do |filepath|
29
+ next if FileTest.directory?(file)
30
+ self.store(filepath)
31
+ end
32
+ }
33
+ end
34
+
35
+ def ensure_bucket(bucket)
36
+ AWS::S3::Bucket.find(bucket)
37
+ rescue
38
+ Ruhoh::Friend.say {
39
+ yellow "'#{@bucket}' bucket not found, trying to create..."
40
+ }
41
+ AWS::S3::Bucket.create(bucket, :access => :public_read)
42
+
43
+ if AWS::S3::Service.response.success?
44
+ Ruhoh::Friend.say { green "Bucket created!" }
45
+ else
46
+ Ruhoh::Friend.say {
47
+ red "Bucket creation failed!"
48
+ plain "Perhaps you will need to manually create the bucket."
49
+ }
50
+ exit
51
+ end
52
+ end
53
+
54
+ # save/update a file to s3
55
+ def store(filepath)
56
+ File.open(filepath) do |file|
57
+ AWS::S3::S3Object.store(filepath, file, @bucket, :access => :public_read)
58
+ end
59
+
60
+ if AWS::S3::Service.response.success?
61
+ Ruhoh::Friend.say { green "#{filepath}: success!" }
62
+ else
63
+ Ruhoh::Friend.say { green "#{filepath}: failure!" }
64
+ end
65
+ end
66
+
67
+ end #S3
68
+
69
+ end #Deployers
70
+
71
+ end #Ruhoh
@@ -0,0 +1,71 @@
1
+ class Ruhoh
2
+
3
+ # The Friend is good for conversation.
4
+ # He tells you what's going on.
5
+ # Implementation is largely copied from rspec gem: http://rspec.info/
6
+ class Friend
7
+
8
+ class << self
9
+
10
+ def say(&block)
11
+ self.instance_eval(&block)
12
+ end
13
+
14
+ # TODO: Adds ability to disable if color is not supported?
15
+ def color_enabled?
16
+ true
17
+ end
18
+
19
+ def list(caption, listings)
20
+ red(" " + caption)
21
+ listings.each do |pair|
22
+ cyan(" - " + pair[0])
23
+ cyan(" " + pair[1])
24
+ end
25
+ end
26
+
27
+ def color(text, color_code)
28
+ puts color_enabled? ? "#{color_code}#{text}\e[0m" : text
29
+ end
30
+
31
+ def plain(text)
32
+ puts text
33
+ end
34
+
35
+ def bold(text)
36
+ color(text, "\e[1m")
37
+ end
38
+
39
+ def red(text)
40
+ color(text, "\e[31m")
41
+ end
42
+
43
+ def green(text)
44
+ color(text, "\e[32m")
45
+ end
46
+
47
+ def yellow(text)
48
+ color(text, "\e[33m")
49
+ end
50
+
51
+ def blue(text)
52
+ color(text, "\e[34m")
53
+ end
54
+
55
+ def magenta(text)
56
+ color(text, "\e[35m")
57
+ end
58
+
59
+ def cyan(text)
60
+ color(text, "\e[36m")
61
+ end
62
+
63
+ def white(text)
64
+ color(text, "\e[37m")
65
+ end
66
+
67
+ end #self
68
+
69
+ end #Friend
70
+
71
+ end #Ruhoh
data/lib/ruhoh/page.rb CHANGED
@@ -1,35 +1,59 @@
1
1
  class Ruhoh
2
2
 
3
3
  class Page
4
- attr_reader :data, :content, :sub_layout, :master_layout
4
+ attr_reader :id, :data, :content, :sub_layout, :master_layout
5
+ attr_accessor :templater, :converter
5
6
 
7
+ def initialize
8
+ @templater = Ruhoh::Templaters::Base
9
+ @converter = Ruhoh::Converter
10
+ end
11
+
6
12
  # Public: Change this page using an id.
7
13
  def change(id)
8
14
  @data = nil
9
- @data = id =~ Regexp.new("^#{Ruhoh.folders.posts}") ? Ruhoh::DB.posts['dictionary'][id] : Ruhoh::DB.pages[id]
15
+
16
+ @data = if id =~ Regexp.new("^#{Ruhoh.folders.posts}")
17
+ Ruhoh::DB.posts['dictionary'][id]
18
+ elsif id =~ Regexp.new("^#{Ruhoh.folders.drafts}")
19
+ Ruhoh::DB.drafts[id]
20
+ else
21
+ Ruhoh::DB.pages[id]
22
+ end
23
+
10
24
  raise "Page #{id} not found in database" unless @data
11
25
  @id = id
12
26
  end
13
27
 
14
28
  # Public: Change this page using a URL.
15
29
  def change_with_url(url)
16
- url = '/index.html' if url == '/'
17
- id = Ruhoh::DB.routes[url]
30
+ id = if url =~ Regexp.new("^/#{Ruhoh.folders.drafts}")
31
+ url.gsub(/^\//,'')
32
+ else
33
+ Ruhoh::DB.routes[url]
34
+ end
35
+
18
36
  raise "Page id not found for url: #{url}" unless id
19
37
  self.change(id)
20
38
  end
21
39
 
22
40
  def render
41
+ raise "ID is null: Id must be set via page.change(id) or page.change_with_url(url)" if @id.nil?
23
42
  self.process_layouts
24
43
  self.process_content
25
- Ruhoh::Templater.expand_and_render(self)
44
+ @templater.render(self)
26
45
  end
27
46
 
28
47
  def process_layouts
29
- @sub_layout = Ruhoh::DB.layouts[@data['layout']]
30
-
31
- if @sub_layout['data']['layout']
48
+ raise "ID is null: Id must be set via page.change(id) or page.change_with_url(url)" if @id.nil?
49
+ if @data['layout']
50
+ @sub_layout = Ruhoh::DB.layouts[@data['layout']]
51
+ raise "Layout does not exist: #{@data['layout']}" unless @sub_layout
52
+ end
53
+
54
+ if @sub_layout && @sub_layout['data']['layout']
32
55
  @master_layout = Ruhoh::DB.layouts[@sub_layout['data']['layout']]
56
+ raise "Layout does not exist: #{@sub_layout['data']['layout']}" unless @master_layout
33
57
  end
34
58
  end
35
59
 
@@ -37,14 +61,18 @@ class Ruhoh
37
61
  # in order to invoke converters on the result.
38
62
  # Converters (markdown) always choke on the templating language.
39
63
  def process_content
40
- @content = Ruhoh::Utils.parse_file(Ruhoh.paths.site_source, @id)['content']
41
- @content = Ruhoh::Templater.render(@content, self)
42
- @content = Ruhoh::Converter.convert(self)
64
+ raise "ID is null: Id must be set via page.change(id) or page.change_with_url(url)" if @id.nil?
65
+ data = Ruhoh::Utils.parse_file(Ruhoh.paths.site_source, @id)
66
+ raise "Invalid Frontmatter in page: #{@id}" if data.empty?
67
+
68
+ @content = @templater.parse(data['content'], self)
69
+ @content = @converter.convert(self)
43
70
  end
44
71
 
45
72
  # Public: Return page attributes suitable for inclusion in the
46
73
  # 'payload' of the given templater.
47
74
  def attributes
75
+ raise "ID is null: Id must be set via page.change(id) or page.change_with_url(url)" if @id.nil?
48
76
  @data['content'] = @content
49
77
  @data
50
78
  end
@@ -53,7 +81,9 @@ class Ruhoh
53
81
  #
54
82
  # Returns: [String] The relative path to the compiled file for this page.
55
83
  def compiled_path
84
+ raise "ID is null: Id must be set via page.change(id) or page.change_with_url(url)" if @id.nil?
56
85
  path = CGI.unescape(@data['url']).gsub(/^\//, '') #strip leading slash.
86
+ path = "index.html" if path.empty?
57
87
  path += '/index.html' unless path =~ /\.html$/
58
88
  path
59
89
  end
@@ -0,0 +1,54 @@
1
+ class Ruhoh
2
+ module Parsers
3
+ module Drafts
4
+
5
+ def self.generate
6
+ raise "Ruhoh.config cannot be nil.\n To set config call: Ruhoh.setup" unless Ruhoh.config
7
+
8
+ dictionary = {}
9
+ invalid = []
10
+
11
+ self.files.each do |filename|
12
+ parsed_page = Ruhoh::Utils.parse_file(filename)
13
+ if parsed_page.empty?
14
+ error = "Invalid YAML Front Matter. Ensure this page has valid YAML, even if it's empty."
15
+ invalid << [filename, error] ; next
16
+ end
17
+
18
+ data = parsed_page['data']
19
+ data['id'] = filename
20
+ data['url'] = "/#{filename}"
21
+ dictionary[filename] = data
22
+ end
23
+
24
+ self.report(dictionary, invalid)
25
+ dictionary
26
+ end
27
+
28
+ def self.files
29
+ FileUtils.cd(Ruhoh.paths.site_source) {
30
+ return Dir["#{Ruhoh.folders.drafts}/**/*.*"].select { |filename|
31
+ next if FileTest.directory?(filename)
32
+ next if ['.'].include? filename[0]
33
+ true
34
+ }
35
+ }
36
+ end
37
+
38
+ def self.report(dictionary, invalid)
39
+ output = "#{dictionary.count}/#{dictionary.count + invalid.count} drafts processed."
40
+ if dictionary.empty? && invalid.empty?
41
+ Ruhoh::Friend.say { plain "0 drafts to process." }
42
+ elsif invalid.empty?
43
+ Ruhoh::Friend.say { green output }
44
+ else
45
+ Ruhoh::Friend.say {
46
+ yellow output
47
+ list "Drafts not processed:", invalid
48
+ }
49
+ end
50
+ end
51
+
52
+ end #Drafts
53
+ end #Parsers
54
+ end #Ruhoh
@@ -1,25 +1,30 @@
1
1
  class Ruhoh
2
-
3
2
  module Parsers
4
-
5
3
  module Layouts
6
4
 
7
5
  # Generate layouts only from the active theme.
8
6
  def self.generate
9
7
  layouts = {}
8
+
9
+ self.files.each do |filename|
10
+ id = File.basename(filename, File.extname(filename))
11
+ layouts[id] = Ruhoh::Utils.parse_file(Ruhoh.paths.layouts, filename)
12
+ raise "Invalid Frontmatter in layout: #{filename}" if layouts[id].empty?
13
+ end
14
+
15
+ layouts
16
+ end
17
+
18
+ def self.files
10
19
  FileUtils.cd(Ruhoh.paths.layouts) {
11
- Dir.glob("**/*.*") { |filename|
20
+ return Dir["**/*.*"].select { |filename|
12
21
  next if FileTest.directory?(filename)
13
22
  next if ['_','.'].include? filename[0]
14
- id = File.basename(filename, File.extname(filename))
15
- layouts[id] = Ruhoh::Utils.parse_file(Ruhoh.paths.layouts, filename)
23
+ true
16
24
  }
17
25
  }
18
- layouts
19
26
  end
20
-
27
+
21
28
  end #Layouts
22
-
23
29
  end #Parsers
24
-
25
30
  end #Ruhoh
@@ -8,59 +8,71 @@ class Ruhoh
8
8
  #
9
9
  def self.generate
10
10
  raise "Ruhoh.config cannot be nil.\n To set config call: Ruhoh.setup" unless Ruhoh.config
11
- puts "=> Generating Pages..."
12
11
 
13
- invalid_pages = []
12
+ pages = self.files
13
+ invalid = []
14
14
  dictionary = {}
15
- total_pages = 0
16
- FileUtils.cd(Ruhoh.paths.site_source) {
17
- Dir.glob("**/*.*") { |filename|
18
- next unless self.is_valid_page?(filename)
19
- total_pages += 1
20
15
 
21
- File.open(filename) do |page|
22
- front_matter = page.read.match(Ruhoh::Utils::FMregex)
23
- if !front_matter
24
- invalid_pages << filename ; next
25
- end
16
+ pages.each do |filename|
17
+ parsed_page = Ruhoh::Utils.parse_file(filename)
18
+ if parsed_page.empty?
19
+ error = "Invalid Yaml Front Matter.\n Ensure this page has valid YAML, even if it's empty."
20
+ invalid << [filename, error] ; next
21
+ end
22
+
23
+ parsed_page['data']['id'] = filename
24
+ parsed_page['data']['url'] = self.permalink(parsed_page['data'])
25
+ parsed_page['data']['title'] = parsed_page['data']['title'] || self.titleize(filename)
26
26
 
27
- data = YAML.load(front_matter[0].gsub(/---\n/, "")) || {}
28
- data['id'] = filename
29
- data['url'] = self.permalink(data)
30
- data['title'] = data['title'] || self.titleize(filename)
31
-
32
- dictionary[filename] = data
33
- end
27
+ dictionary[filename] = parsed_page['data']
28
+ end
29
+
30
+ report = "#{pages.count - invalid.count }/#{pages.count} pages processed."
31
+
32
+ if pages.count.zero? && invalid.empty?
33
+ Ruhoh::Friend.say { plain "0 pages to process." }
34
+ elsif invalid.empty?
35
+ Ruhoh::Friend.say { green report }
36
+ else
37
+ Ruhoh::Friend.say {
38
+ yellow report
39
+ list "Pages not processed:", invalid
34
40
  }
35
- }
41
+ end
36
42
 
37
- if invalid_pages.empty?
38
- puts "=> #{total_pages - invalid_pages.count }/#{total_pages} pages processed."
39
- else
40
- puts "=> Invalid pages not processed:"
41
- puts invalid_pages.to_yaml
42
- end
43
-
44
43
  dictionary
45
44
  end
46
-
47
- def self.is_valid_page?(filename)
48
- return false if FileTest.directory?(filename)
49
- return false if ['_', '.'].include? filename[0]
50
- return false if Ruhoh.filters.pages['names'].include? filename
51
- Ruhoh.filters.pages['regexes'].each {|regex| return false if filename =~ regex }
45
+
46
+ def self.files
47
+ FileUtils.cd(Ruhoh.paths.site_source) {
48
+ return Dir["**/*.*"].select { |filename|
49
+ next unless self.is_valid_page?(filename)
50
+ true
51
+ }
52
+ }
53
+ end
54
+
55
+ def self.is_valid_page?(filepath)
56
+ return false if FileTest.directory?(filepath)
57
+ return false if ['_', '.'].include? filepath[0]
58
+ return false if Ruhoh.filters.pages['names'].include? filepath
59
+ Ruhoh.filters.pages['regexes'].each {|regex| return false if filepath =~ regex }
52
60
  true
53
61
  end
54
62
 
55
63
  def self.titleize(filename)
56
- File.basename( filename, File.extname(filename) ).gsub(/[\W\_]/, ' ').gsub(/\b\w/){$&.upcase}
64
+ name = File.basename( filename, File.extname(filename) )
65
+ name = filename.split('/')[-2] if name == 'index' && !filename.index('/').nil?
66
+ name.gsub(/[\W\_]/, ' ').gsub(/\b\w/){$&.upcase}
57
67
  end
58
68
 
59
69
  def self.permalink(page)
60
70
  url = '/' + page['id'].gsub(File.extname(page['id']), '.html')
61
-
62
71
  # sanitize url
63
72
  url = url.split('/').reject{ |part| part =~ /^\.+$/ }.join('/')
73
+ url.gsub!(/\/index.html$/, '')
74
+ url = "/" if url.empty?
75
+
64
76
  url
65
77
  end
66
78
 
@@ -5,7 +5,19 @@ class Ruhoh
5
5
  module Partials
6
6
 
7
7
  def self.generate
8
- self.process(Ruhoh.paths.global_partials).merge( self.process(Ruhoh.paths.partials) )
8
+ self.system_partials.merge(self.global_partials).merge(self.theme_partials)
9
+ end
10
+
11
+ def self.theme_partials
12
+ self.process(Ruhoh.paths.partials)
13
+ end
14
+
15
+ def self.global_partials
16
+ self.process(Ruhoh.paths.global_partials)
17
+ end
18
+
19
+ def self.system_partials
20
+ self.process(File.join(Ruhoh::Root, 'system_partials'))
9
21
  end
10
22
 
11
23
  def self.process(path)
@@ -16,7 +28,7 @@ class Ruhoh
16
28
  Dir.glob("**/*").each { |filename|
17
29
  next if FileTest.directory?(filename)
18
30
  next if ['.'].include? filename[0]
19
- partials[filename] = File.open(filename).read
31
+ File.open(filename) { |f| partials[filename] = f.read }
20
32
  }
21
33
  }
22
34
  partials