pebble 0.2.6.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+