pebble 0.2.6.alpha1

Sign up to get free protection for your applications and to get access to all the features.
data/bin/pebble ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pebble'
4
+
5
+ Pebble.application.run
@@ -0,0 +1,40 @@
1
+ require 'cli'
2
+
3
+ module Pebble
4
+
5
+ class CliParser
6
+
7
+ def initialize(arguments = ARGV)
8
+
9
+ @options = CLI.new do
10
+ version(Pebble::VERSION)
11
+ switch(:force, {:short => :f, :description => 'Overwrites "dst-dir" without asking'})
12
+ argument(:src_dir, {:required => false, :description => 'Current working directory is taken if omitted'})
13
+ argument(:dst_dir, {:required => false, :description => 'Name of "src-dir" prefixed with "rendered_" if omitted'})
14
+ end.parse!(arguments)
15
+
16
+ end
17
+
18
+ def force
19
+ !@options.force.nil?
20
+ end
21
+
22
+ def src_dir
23
+ if (@options.src_dir)
24
+ File.expand_path(@options.src_dir)
25
+ else
26
+ Dir.pwd
27
+ end
28
+ end
29
+
30
+ def dst_dir
31
+ if (@options.dst_dir)
32
+ File.expand_path(@options.dst_dir)
33
+ else
34
+ File.join(File.dirname(src_dir), 'rendered_' + File.basename(src_dir))
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,49 @@
1
+ module Pebble
2
+
3
+ class Crawler
4
+
5
+ attr_reader :pages, :layouts, :follow_layouts, :snippets, :slots, :static
6
+
7
+ def initialize(root_path)
8
+ @root = root_path
9
+
10
+ @pages = Array.new
11
+ @layouts = Array.new
12
+ @follow_layouts = Array.new
13
+ @snippets = Array.new
14
+ @slots = Array.new
15
+ @static = Array.new
16
+
17
+ run
18
+ end
19
+
20
+ def run
21
+ Find.find(@root) do |path|
22
+ if FileTest.directory?(path)
23
+ if File.basename(path)[0] == ?.
24
+ Find.prune # Don't look any further into this directory.
25
+ else
26
+ next
27
+ end
28
+ elsif FileTest.file?(path)
29
+ case File.basename(path)
30
+ when /\..+\.layout$/
31
+ @follow_layouts << path
32
+ when /\.layout$/
33
+ @layouts << path
34
+ when /\.snippet$/
35
+ @snippets << path
36
+ when /\..+\.html$/
37
+ @slots << path
38
+ when /\.html$/
39
+ @pages << path
40
+ else
41
+ @static << path
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,41 @@
1
+ require 'pebble/render_item'
2
+
3
+ module Pebble
4
+
5
+ class LayoutRenderItem < RenderItem
6
+
7
+ def initialize(path, next_item)
8
+ super(path)
9
+ @next = next_item
10
+ end
11
+
12
+ def render(indent = "")
13
+ content = String.new
14
+ IO.foreach(@path) do |line|
15
+ if line =~ /(.*)\{\{ content \}\}(.*)/
16
+ unless @next.nil?
17
+ prepend = $1
18
+ append = $2
19
+ if (prepend =~ /^\s*$/) && (append =~ /^\s*$/)
20
+ content << @next.render(indent + prepend) unless @next.nil?
21
+ content << "\n"
22
+ else
23
+ content << "#{indent + prepend}"
24
+ content << @next.render unless @next.nil?
25
+ content << "#{append}\n"
26
+ end
27
+ else
28
+ content << indent
29
+ content << line
30
+ end
31
+ else
32
+ content << indent
33
+ content << line
34
+ end
35
+ end
36
+ content.chomp
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,26 @@
1
+ module Pebble
2
+
3
+ class RenderItem
4
+
5
+ attr_reader :path
6
+
7
+ def initialize(path)
8
+ @path = path
9
+ end
10
+
11
+ def read(indent = "")
12
+ content = String.new
13
+ IO.foreach(@path) do |line|
14
+ content << indent
15
+ content << line
16
+ end
17
+ content.chomp
18
+ end
19
+
20
+ def length
21
+ @path.length
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,119 @@
1
+ module Pebble
2
+
3
+ #--
4
+ # RenderSeq class
5
+ #++
6
+ #
7
+ # A *.html file has zero or more _layouts_, _slots_ and _snippets_ that are
8
+ # rendered together. This class manages all of the parts that are rendered
9
+ # for a single page of output.
10
+ #
11
+ class RenderSeq
12
+
13
+ # Hashes of parts of pages that are to be rendered
14
+ attr_accessor :layout, :snippets, :slots
15
+ #
16
+ attr_reader :page
17
+
18
+ # Set up a new RenderSeq. _page_ is
19
+ def initialize(page)
20
+ @page = page
21
+ @layout = nil
22
+ @snippets = nil
23
+ @slots = nil
24
+
25
+ @content = String.new
26
+ end
27
+
28
+ def render
29
+ render_layout
30
+ render_page
31
+ render_slots
32
+ render_snippets
33
+ end
34
+
35
+ private
36
+
37
+ def render_layout
38
+ @content = @layout.render
39
+ end
40
+
41
+ def render_page
42
+ content = String.new
43
+ @content.each_line do |line|
44
+ if line =~ /(.*)\{\{ content \}\}(.*)/
45
+ prepend = $1
46
+ append = $2
47
+ if (prepend =~ /^\s*$/) && (append =~ /^\s*$/)
48
+ content << @page.read(prepend)
49
+ content << "\n"
50
+ else
51
+ content << "#{prepend}"
52
+ content << @page.read
53
+ content << "#{append}\n"
54
+ end
55
+ else
56
+ content << line
57
+ end
58
+ end
59
+ content.chomp
60
+ @content = content
61
+ end
62
+
63
+ def render_slots
64
+ content = String.new
65
+ @content.each_line do |line|
66
+ if line =~ /(.*)\{\{ content "(.*)" \}\}(.*)/
67
+ prepend = $1
68
+ slot = $2
69
+ append = $3
70
+ if (!@slots.nil?) && (@slots.has_key?(slot))
71
+ if (prepend =~ /^\s*$/) && (append =~ /^\s*$/)
72
+ content << @slots[slot].read(prepend)
73
+ content << "\n"
74
+ else
75
+ content << "#{prepend}"
76
+ content << @slots[slot].read
77
+ content << "#{append}\n"
78
+ end
79
+ else
80
+ $stderr.puts "WARNING: Slot with name '#{slot}' not found"
81
+ end
82
+ else
83
+ content << line
84
+ end
85
+ end
86
+ content.chomp
87
+ @content = content
88
+ end
89
+
90
+ def render_snippets
91
+ content = String.new
92
+ @content.each_line do |line|
93
+ if line =~ /(.*)\{\{ snippet "(.*)" \}\}(.*)/
94
+ prepend = $1
95
+ snippet = $2
96
+ append = $3
97
+ if (!@snippets.nil?) && (@snippets.has_key?(snippet))
98
+ if (prepend =~ /^\s*$/) && (append =~ /^\s*$/)
99
+ content << @snippets[snippet].read(prepend)
100
+ content << "\n"
101
+ else
102
+ content << "#{prepend}"
103
+ content << @snippets[snippet].read
104
+ content << "#{append}\n"
105
+ end
106
+ else
107
+ $stderr.puts "WARNING: Snippet with name '#{snippet}' not found"
108
+ end
109
+ else
110
+ content << line
111
+ end
112
+ end
113
+ content.chomp
114
+ @content = content
115
+ end
116
+
117
+ end
118
+
119
+ end
@@ -0,0 +1,69 @@
1
+ module Pebble
2
+
3
+ class Site
4
+
5
+ # The current value of the directory tree to be rendered
6
+ attr_reader :site_root
7
+
8
+ # The current value of where to save the rendered site
9
+ attr_reader :rendered_root
10
+
11
+ def initialize(site_root, rendered_root)
12
+ @site_root = site_root
13
+ @rendered_root = rendered_root
14
+ @sequences = Array.new
15
+ end
16
+
17
+ # Add a RenderSeq to Site that needs to be rendered
18
+ def add_sequence(seq)
19
+ @sequences << seq
20
+ end
21
+
22
+ # Iterates over every RenderSeq and calls it to render itself
23
+ def render
24
+ @sequences.each do |seq|
25
+ target = get_and_prepare_target(seq.page.path)
26
+ content = seq.render
27
+ File.open(target, "w") do |file|
28
+ file.puts content
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ # Checks and possibly deletes everything that is at the location
35
+ # of _rendered_root_ where the new rendered site goes.
36
+ # If _force_ is set to +true+, no question is asked if the directory
37
+ # already exists.
38
+ def clean_target(force = false)
39
+ if File.exists?(@rendered_root)
40
+ unless force
41
+ print "Are you sure to overwrite directory '#{@rendered_root}' [yN] ? "
42
+ exit unless $stdin.gets.chomp.downcase == 'y'
43
+ end
44
+ Dir.rmdir_rec(@rendered_root)
45
+ end
46
+ end
47
+
48
+ # Copies the static files to the destination
49
+ def copy_static(files)
50
+
51
+ files.each do |file|
52
+ target = get_and_prepare_target(file)
53
+ File.cp(file, target)
54
+ end
55
+
56
+ end
57
+
58
+ private
59
+
60
+ def get_and_prepare_target(source)
61
+ filename = source.sub(@site_root, @rendered_root)
62
+ dirname = File.dirname(filename)
63
+ Dir.mkdir_rec(dirname) unless File.directory?(dirname)
64
+ filename
65
+ end
66
+
67
+ end
68
+
69
+ end
@@ -0,0 +1,3 @@
1
+ module Pebble
2
+ VERSION = '0.2.6.alpha1'
3
+ end
data/lib/pebble.rb ADDED
@@ -0,0 +1,217 @@
1
+ #--
2
+ # Copyright (c) 2007, Christian Luginbuehl
3
+ #
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions are met:
8
+ #
9
+ # * Redistributions of source code must retain the above copyright notice,
10
+ # this list of conditions and the following disclaimer.
11
+ # * Redistributions in binary form must reproduce the above copyright
12
+ # notice, this list of conditions and the following disclaimer in the
13
+ # documentation and/or other materials provided with the distribution.
14
+ # * No names of its contributors may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ #++
29
+ #
30
+ # = Pebble -- Simple static CMS
31
+ #
32
+ # This is the main file for the Pebble application. Normally it is referenced
33
+ # as a library via a require statement and invoked using the +pebble+
34
+ # executable.
35
+
36
+ require 'find'
37
+ require 'fileutils'
38
+ require 'pebble/site'
39
+ require 'pebble/cli_parser'
40
+ require 'pebble/crawler'
41
+ require 'pebble/render_item'
42
+ require 'pebble/layout_render_item'
43
+ require 'pebble/render_seq'
44
+ require 'pebble/version'
45
+
46
+ ###############################################################################
47
+ # Some extensions to Dir
48
+ #
49
+ class Dir
50
+
51
+ # Recursively delete a directory tree including files (like rm -rf)
52
+ def Dir.rmdir_rec(dir)
53
+ Dir.foreach(dir) do |e|
54
+ next if [".", ".."].include? e
55
+ fullname = dir + File::Separator + e
56
+ if FileTest::directory?(fullname)
57
+ Dir.rmdir_rec(fullname)
58
+ else
59
+ File.delete(fullname)
60
+ end
61
+ end
62
+ Dir.delete(dir)
63
+ end
64
+
65
+ # Recursively create directories (like mkdir -p)
66
+ def Dir.mkdir_rec(dir)
67
+ parent = File.expand_path(File.join(dir, ".."))
68
+ Dir.mkdir_rec(File.expand_path(File.join(dir, ".."))) unless File.directory?(parent)
69
+ Dir.mkdir(dir)
70
+ end
71
+
72
+ end
73
+
74
+ ##############################################################################
75
+ # Pebble module
76
+ #
77
+ module Pebble
78
+
79
+ # Pebble module singleton methods.
80
+ #
81
+ class << self
82
+ # Current Pebble Application
83
+ def application
84
+ @application ||= Pebble::Application.new
85
+ end
86
+ end
87
+
88
+ class Application
89
+
90
+ def initialize
91
+ parser = CliParser.new()
92
+
93
+ @force = parser.force
94
+ @src = parser.src_dir
95
+ @dst = parser.dst_dir
96
+ end
97
+
98
+ # Runs *pebble* by first putting together all RenderSeq's, preparing the
99
+ # destination directory and then rendering and saving the files. As its
100
+ # last step, all the static files are copied to the destination.
101
+ def run
102
+ @crawler = Crawler.new(@src)
103
+
104
+ @site = Site.new(@src, @dst)
105
+
106
+ find_render_sequences
107
+
108
+ @site.clean_target(@force)
109
+ @site.render
110
+ @site.copy_static(@crawler.static)
111
+
112
+ end
113
+
114
+ private
115
+
116
+ # Find the render sequences for every page to be rendered
117
+ def find_render_sequences
118
+ @crawler.pages.each do |path|
119
+
120
+ page = RenderItem.new(path)
121
+
122
+ seq = RenderSeq.new(page)
123
+
124
+ filename = File.basename(path, ".html")
125
+ dirname = File.dirname(path)
126
+
127
+ # search in local dir for [filename].layout
128
+ if @crawler.layouts.include?(File.join(dirname, "#{filename}.layout"))
129
+ item = LayoutRenderItem.new(File.join(dirname, "#{filename}.layout"), nil)
130
+ seq.layout = item
131
+ end
132
+
133
+ # search in local dir for [filename].follow.layout
134
+ if seq.layout.nil? && @crawler.follow_layouts.include?(File.join(dirname, "#{filename}.follow.layout"))
135
+ item = LayoutRenderItem.new(File.join(dirname, "#{filename}.follow.layout"), item)
136
+ end
137
+
138
+ # search in local dir for default.layout
139
+ if seq.layout.nil? && @crawler.layouts.include?(File.join(dirname, "default.layout"))
140
+ item = LayoutRenderItem.new(File.join(dirname, "default.layout"), item)
141
+ seq.layout = item
142
+ end
143
+
144
+ # search in local dir for default.follow.layout
145
+ if seq.layout.nil? && @crawler.follow_layouts.include?(File.join(dirname, "default.follow.layout"))
146
+ item = LayoutRenderItem.new(File.join(dirname, "default.follow.layout"), item)
147
+ end
148
+
149
+ if seq.layout.nil?
150
+ # search in parents for default.layout
151
+ possible_layouts = Array.new
152
+ @crawler.layouts.each do |layout|
153
+ if (layout =~ /default\.layout$/) && (dirname.include? File.dirname(layout))
154
+ possible_layouts << layout
155
+ end
156
+ end
157
+
158
+ possible_layouts.sort { |a, b| b.length <=> a.length }
159
+
160
+ # search in parents for default.follow.layout
161
+ possible_follow_layouts = Array.new
162
+ @crawler.follow_layouts.each do |layout|
163
+ if (layout =~ /default\.follow\.layout$/) && (dirname.include? File.dirname(layout)) && (!File.dirname(layout).include? dirname)
164
+ possible_follow_layouts << layout
165
+ end
166
+ end
167
+
168
+ possible_follow_layouts.sort { |a, b| b.length <=> a.length }
169
+
170
+ possible_follow_layouts.each do |layout|
171
+ if layout.length - 7 > possible_layouts[0].length
172
+ item = LayoutRenderItem.new(layout, item)
173
+ end
174
+ end
175
+
176
+ item = LayoutRenderItem.new(possible_layouts[0], item) unless possible_layouts.empty?
177
+
178
+ end
179
+
180
+ seq.layout = item if seq.layout.nil?
181
+
182
+ # search for all possible snippets (in case of same name, higher level will be ignored)
183
+ possible_snippets = Hash.new
184
+ @crawler.snippets.each do |snippet|
185
+ if dirname.include? File.dirname(snippet)
186
+ name = File.basename(snippet, ".snippet")
187
+ if (!possible_snippets.has_key?(name)) || (possible_snippets[name].length < snippet.length)
188
+ possible_snippets[name] = RenderItem.new(snippet)
189
+ end
190
+ end
191
+ end
192
+
193
+ seq.snippets = possible_snippets
194
+
195
+ # search for all possible slots (in case of 'default' and specific name, the latter is used)
196
+ possible_slots = Hash.new
197
+ @crawler.slots.each do |slot|
198
+ if dirname.eql? File.dirname(slot)
199
+ if File.basename(slot, ".html").include? filename
200
+ name = File.basename(slot, ".html").sub("#{filename}.", '')
201
+ possible_slots[name] = RenderItem.new(slot)
202
+ elsif File.basename(slot, ".html").include? 'default'
203
+ name = File.basename(slot, ".html").sub("default.", '')
204
+ possible_slots[name] = RenderItem.new(slot)
205
+ end
206
+ end
207
+ end
208
+
209
+ seq.slots = possible_slots
210
+
211
+ @site.add_sequence(seq)
212
+ end
213
+ end
214
+
215
+ end
216
+
217
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pebble
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: true
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 6
9
+ - alpha1
10
+ version: 0.2.6.alpha1
11
+ platform: ruby
12
+ authors:
13
+ - Christian Luginbuehl
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-09-15 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: cli
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ segments:
30
+ - 1
31
+ - 1
32
+ version: "1.1"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description: A simple static CMS
36
+ email: dinkel@pimprecords.com
37
+ executables:
38
+ - pebble
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - lib/pebble.rb
45
+ - lib/pebble/site.rb
46
+ - lib/pebble/layout_render_item.rb
47
+ - lib/pebble/version.rb
48
+ - lib/pebble/render_seq.rb
49
+ - lib/pebble/render_item.rb
50
+ - lib/pebble/cli_parser.rb
51
+ - lib/pebble/crawler.rb
52
+ - bin/pebble
53
+ has_rdoc: true
54
+ homepage: https://github.com/dinkel/pebble/
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options: []
59
+
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">"
74
+ - !ruby/object:Gem::Version
75
+ segments:
76
+ - 1
77
+ - 3
78
+ - 1
79
+ version: 1.3.1
80
+ requirements: []
81
+
82
+ rubyforge_project:
83
+ rubygems_version: 1.3.7
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Pebble
87
+ test_files: []
88
+