plato 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.3
1
+ 0.2.4
@@ -0,0 +1,78 @@
1
+ module Plato
2
+ module HeadersCodec
3
+ extend self
4
+
5
+ def deflate(hash)
6
+ hash = stringify_keys(hash)
7
+ body = hash.delete('body')
8
+
9
+ [].tap do |buffer|
10
+ buffer << hash.map do |key, value|
11
+ "#{header_for(key)}: #{Sanitize.header(value)}"
12
+ end.join("\n")
13
+
14
+ buffer << "\n\n" << Sanitize.body(body) if body
15
+ buffer << "\n" unless buffer.last =~ /\n\Z/
16
+ end
17
+ end
18
+
19
+ def inflate(string)
20
+ {}.tap do |result|
21
+ headers, body = string.split(/\n\n/, 2)
22
+
23
+ headers.split("\n").each do |line|
24
+ header, val = line.split(/:\s*/, 2)
25
+
26
+ result.update hash_key_for(header) => deserialize_value(val)
27
+ end
28
+
29
+ result['body'] = body.chomp if body
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def stringify_keys(hash)
36
+ hash.inject({}) {|h, (k, v)| h.update k.to_s => v }
37
+ end
38
+
39
+ def header_for(attr)
40
+ attr.to_s.gsub('_', ' ').gsub(/\b([a-z])/) {|m| m.capitalize }
41
+ end
42
+
43
+ def hash_key_for(header)
44
+ header.gsub(/\s+/, '_').downcase.to_s
45
+ end
46
+
47
+ def deserialize_value(val)
48
+ YAML.load(val) rescue val
49
+ end
50
+
51
+ module Sanitize
52
+ extend self
53
+
54
+ def header(header_val)
55
+ header_val.gsub(/(\r|\n)/) {|m| {"\r" => '\r', "\n" => '\n'}[m] }
56
+ end
57
+
58
+ def path_elem(elem)
59
+ elem.to_s.gsub(/\s+/, '_').gsub(/[^a-zA-Z0-9_-]/,'')
60
+ end
61
+
62
+ def body(body)
63
+ body # do we really need to do anything here?
64
+ end
65
+
66
+ def method_missing(method, value)
67
+ warn "Warning: not sanitizing #{method}."
68
+ value.to_s
69
+ end
70
+
71
+ private
72
+
73
+ def warn(warning)
74
+ $stderr.puts warning
75
+ end
76
+ end
77
+ end
78
+ end
@@ -2,27 +2,23 @@ module Plato
2
2
  class Manifest
3
3
  include Enumerable
4
4
 
5
- attr_reader :contents, :codec
6
-
7
- def initialize(contents = nil, opts = {})
8
- opts = { :codec => opts } unless opts.is_a? Hash
9
- @codec = opts[:codec]
5
+ attr_reader :contents
6
+ alias to_h contents
7
+ alias to_hash contents
10
8
 
9
+ def initialize(contents = nil)
11
10
  @contents =
12
11
  if contents.nil?
13
12
  {}
14
- elsif contents.is_a? Hash
15
- contents
16
- elsif contents.is_a? String
17
- Repo.new(contents, @codec).all &opts[:filter]
13
+ elsif contents.respond_to? :to_hash
14
+ contents.to_hash
18
15
  else
19
16
  raise ArgumentError, "invalid contents"
20
17
  end
21
18
  end
22
19
 
23
- def save_to(path, codec = nil)
24
- repo = Repo.new(path, codec || self.codec)
25
- @contents.map {|path, hash| repo.save(path, hash); path }
20
+ def save_to(path)
21
+ Repo.new(path).set_all(contents)
26
22
  end
27
23
 
28
24
  def [](key)
@@ -36,18 +32,14 @@ module Plato
36
32
 
37
33
  # if given a block, block should return a hash of the new path and
38
34
  # new data, or nil if the file should be skipped
39
- def map(new_codec = nil)
35
+ def map
40
36
  new_contents =
41
- if block_given?
42
- @contents.inject({}) do |hash, (path, data)|
43
- new_path_data = yield(path, data)
44
- new_path_data ? hash.update(new_path_data) : hash
45
- end
46
- else
47
- @contents.dup
37
+ @contents.inject({}) do |hash, (path, data)|
38
+ new_path_data = yield(path, data)
39
+ new_path_data ? hash.update(new_path_data) : hash
48
40
  end
49
41
 
50
- self.class.new(new_contents, new_codec || self.codec)
42
+ self.class.new(new_contents)
51
43
  end
52
44
 
53
45
  def each(&block)
@@ -74,14 +74,4 @@ module Plato
74
74
  %{<script #{attribute_pairs(opts)}></script>}
75
75
  end
76
76
  end
77
-
78
- class RubyTiltTemplate < Tilt::Template
79
- def prepare; end
80
-
81
- def precompiled_template(locals)
82
- data
83
- end
84
- end
85
-
86
- ::Tilt.register('rb', RubyTiltTemplate)
87
77
  end
data/lib/plato/repo.rb CHANGED
@@ -2,33 +2,76 @@ require 'yaml'
2
2
  require 'fileutils'
3
3
 
4
4
  module Plato
5
+ class FileObject
6
+ attr_reader :path, :data
7
+
8
+ def initialize(opts = {})
9
+ @path = opts[:path]
10
+ @data = opts[:data]
11
+ end
12
+
13
+ def data
14
+ @data ||= (File.read(@path) if @path)
15
+ end
16
+ alias read data
17
+
18
+ def write_to(to_path)
19
+ if @data
20
+ FileUtils.mkdir_p(File.dirname(to_path))
21
+ File.open(to_path, 'w') {|f| f.write(@data) }
22
+ elsif @path
23
+ FileUtils.mkdir_p(File.dirname(to_path))
24
+ FileUtils.cp(@path, to_path)
25
+ else
26
+ raise "cannot write out empty file object"
27
+ end
28
+ end
29
+ end
30
+
5
31
  class Repo
6
- attr_reader :root, :codec
32
+ class NotFound < StandardError; end
7
33
 
8
- CODEC_MAP = {}
34
+ attr_reader :root
9
35
 
10
- def initialize(root, codec = nil)
36
+ def initialize(root)
11
37
  unless root == File.expand_path(root)
12
38
  raise ArgumentError, "root is not an absolute path"
13
39
  end
14
40
 
15
- @codec = CODEC_MAP[codec] || StringCodec
16
41
  @root = root.sub(/\/+\Z/, '') #remove trailing slash(es)
17
42
  end
18
43
 
19
- def load(path)
44
+ def get(path)
20
45
  path = expanded_path(path)
21
- if File.exist? path
22
- @codec.read(path)
23
- else
24
- raise Filestore::NotFound
46
+ raise NotFound unless File.exist? path
47
+
48
+ FileObject.new(:path => path)
49
+ end
50
+
51
+ def set(path, data)
52
+ fo = data.respond_to?(:write_to) ?
53
+ data : FileObject.new(:data => data)
54
+
55
+ fo.write_to(expanded_path(path))
56
+ end
57
+
58
+ def all
59
+ paths = Dir[File.join(root, '**/*')].select {|e| File.file? e }
60
+
61
+ paths.inject({}) do |hash, path|
62
+ relative = relative_path(path)
63
+ hash.update relative => get(relative)
25
64
  end
26
65
  end
66
+ alias to_h all
67
+ alias to_hash all
27
68
 
28
- def save(path, data)
29
- path = expanded_path(path)
30
- FileUtils.mkdir_p(File.dirname(path))
31
- @codec.write(path, data)
69
+ def each(&block)
70
+ all.each(&block)
71
+ end
72
+
73
+ def set_all(hash)
74
+ hash.each {|path, data| set(path, data) }
32
75
  end
33
76
 
34
77
  def destroy(path)
@@ -45,18 +88,6 @@ module Plato
45
88
  end
46
89
  end
47
90
 
48
- def all
49
- paths = Dir[File.join(root, '**/*')].select {|e| File.file? e }
50
-
51
- if block_given?
52
- paths = paths.select {|p| yield relative_path(p) }
53
- end
54
-
55
- paths.inject({}) do |hash, path|
56
- hash.update relative_path(path) => load(path)
57
- end
58
- end
59
-
60
91
  private
61
92
 
62
93
  def expanded_path(path)
@@ -68,131 +99,5 @@ module Plato
68
99
  raise ArgumentError, "path must subpath of root" if relative == path
69
100
  end
70
101
  end
71
-
72
- module RefCodec
73
- extend self
74
-
75
- def write(path, ref)
76
- FileUtils.cp(ref, path)
77
- end
78
-
79
- def read(ref)
80
- ref
81
- end
82
- end
83
- CODEC_MAP[:refs] = RefCodec
84
-
85
- module StringCodec
86
- extend self
87
-
88
- def write(path, data)
89
- File.open(path, 'w') {|f| f.write data }
90
- end
91
-
92
- def read(path)
93
- File.read(path)
94
- end
95
- end
96
- CODEC_MAP[:string] = StringCodec
97
-
98
- module TemplateCodec
99
- extend self
100
-
101
- def read(path)
102
- if template_class = Tilt[path]
103
- template_class.new(path)
104
- else
105
- path
106
- end
107
- end
108
-
109
- def write(path, data)
110
- raise "Templates cannot be directly written"
111
- end
112
- end
113
- CODEC_MAP[:template] = TemplateCodec
114
-
115
- module HashCodec
116
- extend StringCodec
117
- extend self
118
-
119
- def write(path, hash)
120
- hash = stringify_keys(hash)
121
- body = hash.delete('body')
122
-
123
- data = [].tap do |buffer|
124
- buffer << hash.map do |key, value|
125
- "#{header_for(key)}: #{Sanitize.header(value)}"
126
- end.join("\n")
127
-
128
- buffer << "\n\n" << Sanitize.body(body) if body
129
- buffer << "\n" unless buffer.last =~ /\n\Z/
130
- end.join
131
-
132
- super(path, data)
133
- end
134
-
135
- def read(path)
136
- string = super
137
-
138
- {}.tap do |result|
139
- headers, body = string.split(/\n\n/, 2)
140
-
141
- headers.split("\n").each do |line|
142
- header, val = line.split(/:\s*/, 2)
143
-
144
- result.update hash_key_for(header) => deserialize_value(val)
145
- end
146
-
147
- result['body'] = body.chomp if body
148
- end
149
- end
150
-
151
- private
152
-
153
- def stringify_keys(hash)
154
- hash.inject({}) {|h, (k, v)| h.update k.to_s => v }
155
- end
156
-
157
- def header_for(attr)
158
- attr.to_s.gsub('_', ' ').gsub(/\b([a-z])/) {|m| m.capitalize }
159
- end
160
-
161
- def hash_key_for(header)
162
- header.gsub(/\s+/, '_').downcase.to_s
163
- end
164
-
165
- def deserialize_value(val)
166
- YAML.load(val) rescue val
167
- end
168
- end
169
- CODEC_MAP[:hash] = HashCodec
170
-
171
- module Sanitize
172
- extend self
173
-
174
- def header(header_val)
175
- header_val.gsub(/(\r|\n)/) {|m| {"\r" => '\r', "\n" => '\n'}[m] }
176
- end
177
-
178
- def path_elem(elem)
179
- elem.to_s.gsub(/\s+/, '_').gsub(/[^a-zA-Z0-9_-]/,'')
180
- end
181
-
182
- def body(body)
183
- body # do we really need to do anything here?
184
- end
185
-
186
- def method_missing(method, value)
187
- warn "Warning: not sanitizing #{method}."
188
- value.to_s
189
- end
190
-
191
- private
192
-
193
- def warn(warning)
194
- $stderr.puts warning
195
- end
196
- end
197
102
  end
198
103
  end
data/lib/plato/site.rb CHANGED
@@ -14,29 +14,21 @@ module Plato
14
14
  @config_path = File.join(@template_path, 'config.rb')
15
15
  @content_path = File.join(@root)
16
16
  @resources_path = File.join(@root, "resources")
17
- end
18
17
 
19
- def generate!
20
- RenderContext.load_view_helpers(File.join(template_path, 'view_helpers.rb'))
21
- [ resources.save_to(cache_path),
22
- template_resources.save_to(cache_path),
23
- rendered_templates.save_to(cache_path),
24
- rendered_content.save_to(cache_path)
25
- ].each do |ps|
26
- puts ps.map{|s| " » #{File.join(cache_path,s)}" }
27
- end
18
+ RenderContext.load_view_helpers(File.join(@template_path, 'view_helpers.rb'))
28
19
  end
29
20
 
30
- DETECT_EXT = /(?:(.*)\/)?([^\/]+)\.([^.]+)\Z/
31
- def detect_zip_path(path)
32
- path = "#{path}.zip" if !File.exist? path and File.exist? "#{path}.zip"
33
-
34
- if File.exist? path and !File.directory? path and path.match(DETECT_EXT)
35
- dir, base, ext = path.match(DETECT_EXT).values_at(1,2,3)
36
-
37
- [dir, "#{base}.#{ext}!", base].compact.join("/")
38
- else
39
- path
21
+ def generate!
22
+ [ ["resources", resources],
23
+ ["template resources", template_resources],
24
+ ["rendered templates", rendered_templates],
25
+ ["rendered content", rendered_content]
26
+ ].each do |(name, manifest)|
27
+ puts "## #{name}:"
28
+ manifest.save_to(cache_path)
29
+ manifest.to_h.keys.map{|p| File.join cache_path, p }.each do |path|
30
+ puts " #{path}"
31
+ end
40
32
  end
41
33
  end
42
34
 
@@ -45,46 +37,30 @@ module Plato
45
37
  end
46
38
 
47
39
  def templates
48
- return @templates if @templates
49
-
50
- manifest = Manifest.new template_path, {
51
- :codec => :template,
52
- :filter => lambda {|p| p !~ /\A(config\.rb|view_helpers\.rb)/ }
53
- }
54
-
55
- path_parser = PathTemplate.new(":name*.:format.:engine")
56
- sass_parser = PathTemplate.new(":name*.sass")
57
-
58
- @template_resources = Manifest.new({}, :refs)
59
- @templates = manifest.map do |path, template|
60
- if template.is_a? String
61
- # could not find a template engine, assume we're a raw resource
62
- @template_resources[path] = template
63
- nil
64
- else
65
- if match = path_parser.parse(path)
66
- name, format = match.values_at("name", "format")
67
- { "#{name}.#{format}" => Template.new(template, format) }
68
- else name = sass_parser.parse(path).values_at("name")
69
- { "#{name}.css" => Template.new(template, 'css') }
70
- end
71
- end
72
- end
40
+ initialize_templates! unless @templates
41
+ @templates
73
42
  end
74
43
 
44
+ def template_resources
45
+ initialize_templates! unless @templates
46
+ @template_resources
47
+ end
48
+
75
49
  def content
76
50
  return @content if @content
77
51
 
78
52
  @content = config["content_categories"]
79
53
  categories = @content.values
80
- content_manifests = @content.keys.map do |c|
81
- Manifest.new(File.join(content_path, c), :hash)
54
+ content_repos = @content.keys.map do |c|
55
+ Repo.new(File.join(content_path, c))
82
56
  end
83
57
 
84
- content_manifests.each do |manifest|
85
- manifest.each do |path, content_data|
58
+ content_repos.each do |repo|
59
+ repo.each do |path, file|
86
60
  if category = categories.find {|c| c.match path }
87
- data = category.match(path).merge(content_data)
61
+ data = category.match(path).merge(
62
+ HeadersCodec.inflate(file.read)
63
+ )
88
64
 
89
65
  category.documents << Document.new(category, data)
90
66
  end
@@ -94,16 +70,51 @@ module Plato
94
70
  @content
95
71
  end
96
72
 
97
- def template_resources
98
- templates unless @templates
99
- @template_resources
73
+ def resources
74
+ @resources ||= Manifest.new(Repo.new(resources_path))
100
75
  end
101
76
 
102
- def resources
103
- @resources ||= Manifest.new(resources_path, :refs)
77
+ DETECT_EXT = /(?:(.*)\/)?([^\/]+)\.([^.]+)\Z/
78
+ def detect_zip_path(path)
79
+ path = "#{path}.zip" if !File.exist? path and File.exist? "#{path}.zip"
80
+
81
+ if File.exist? path and !File.directory? path and path.match(DETECT_EXT)
82
+ dir, base, ext = path.match(DETECT_EXT).values_at(1,2,3)
83
+
84
+ [dir, "#{base}.#{ext}!", base].compact.join("/")
85
+ else
86
+ path
87
+ end
104
88
  end
105
89
 
90
+ def initialize_templates!
91
+ path_parser = PathTemplate.new(":name*.:format.:engine")
92
+ sass_parser = PathTemplate.new(":name*.sass")
93
+
94
+ @template_resources = Manifest.new
95
+ @templates = {}
96
+
97
+ Repo.new(template_path).each do |path, file|
98
+ next if path =~ /\A(config\.rb|view_helpers\.rb)/
99
+
100
+ if tilt_class = Tilt[path]
101
+ tilt = tilt_class.new(File.join(template_path, path))
106
102
 
103
+ if match = path_parser.parse(path)
104
+ name, format = match.values_at("name", "format")
105
+ @templates["#{name}.#{format}"] = Template.new(tilt, format)
106
+
107
+ else name = sass_parser.parse(path).values_at("name")
108
+ @templates["#{name}.css"] = Template.new(tilt, 'css')
109
+ end
110
+ else
111
+ # could not find a template engine, assume we're a raw resource
112
+ @template_resources[path] = file
113
+ nil
114
+ end
115
+ end
116
+ end
117
+
107
118
  # helpers
108
119
 
109
120
  Template = Struct.new(:renderer, :format)
@@ -117,7 +128,7 @@ module Plato
117
128
  end
118
129
 
119
130
  def rendered_templates
120
- templates.map(:string) do |path, template|
131
+ Manifest.new(templates).map do |path, template|
121
132
  if path !~ /\A_/
122
133
  { path => render(template, template.format, nil) }
123
134
  end
data/lib/plato.rb CHANGED
@@ -4,9 +4,10 @@ require 'ruby_archive'
4
4
  module Plato
5
5
  require 'plato/config'
6
6
  require 'plato/document'
7
+ require 'plato/headers_codec'
7
8
  require 'plato/manifest'
8
9
  require 'plato/path_template'
9
- require 'plato/repo'
10
10
  require 'plato/rendering'
11
+ require 'plato/repo'
11
12
  require 'plato/site'
12
13
  end
data/plato.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{plato}
8
- s.version = "0.2.3"
8
+ s.version = "0.2.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Matt Freels"]
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
23
23
  "lib/plato.rb",
24
24
  "lib/plato/config.rb",
25
25
  "lib/plato/document.rb",
26
+ "lib/plato/headers_codec.rb",
26
27
  "lib/plato/manifest.rb",
27
28
  "lib/plato/path_template.rb",
28
29
  "lib/plato/rendering.rb",
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plato
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 31
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 3
10
- version: 0.2.3
9
+ - 4
10
+ version: 0.2.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matt Freels
@@ -97,6 +97,7 @@ files:
97
97
  - lib/plato.rb
98
98
  - lib/plato/config.rb
99
99
  - lib/plato/document.rb
100
+ - lib/plato/headers_codec.rb
100
101
  - lib/plato/manifest.rb
101
102
  - lib/plato/path_template.rb
102
103
  - lib/plato/rendering.rb