gumdrop 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,7 +3,8 @@ module Gumdrop
3
3
  class Generator
4
4
  attr_reader :filename, :base_path, :params, :pages
5
5
 
6
- def initialize(content, opts={})
6
+ def initialize(content, site, opts={})
7
+ @site= site
7
8
  @content= content
8
9
  if @content.is_a? Proc
9
10
  @filename= ""
@@ -23,9 +24,13 @@ module Gumdrop
23
24
  run_dsl_from_source IO.readlines(@content.path).join('')
24
25
  end
25
26
  end
27
+
28
+ def site
29
+ @site
30
+ end
26
31
 
27
32
  def data
28
- Gumdrop.data
33
+ @site.data
29
34
  end
30
35
 
31
36
  def set(var_name, value)
@@ -40,16 +45,16 @@ module Gumdrop
40
45
  else
41
46
  "/#{@base_path}/#{name}"
42
47
  end
43
- content= GeneratedContent.new(filepath, block, opts)
48
+ content= GeneratedContent.new(filepath, block, @site, opts)
44
49
  if opts.has_key? :template and !opts[:template].nil?
45
- content.template = if Gumdrop.layouts.has_key?( opts[:template] )
46
- Gumdrop.layouts[ opts[:template] ]
50
+ content.template = if @site.layouts.has_key?( opts[:template] )
51
+ @site.layouts[ opts[:template] ]
47
52
  else
48
- Gumdrop.layouts[ "#{opts[:template]}.template" ]
53
+ @site.layouts[ "#{opts[:template]}.template" ]
49
54
  end.template
50
55
  end
51
-
52
- Gumdrop.site[content.uri]= content
56
+ @site.report "-generated: #{content.uri}", :info
57
+ @site.node_tree[content.uri]= content
53
58
  end
54
59
 
55
60
  # FIXME: Does redirect require abs-paths?
@@ -62,9 +67,9 @@ module Gumdrop
62
67
  EOF
63
68
  end
64
69
  opts[:from]= from
65
- Gumdrop.redirects << opts
70
+ @site.redirects << opts
66
71
  else
67
- Gumdrop.report "You must specify :to in a redirect", :warning
72
+ @site.report "You must specify :to in a redirect", :warning
68
73
  end
69
74
  end
70
75
 
@@ -93,7 +98,7 @@ module Gumdrop
93
98
 
94
99
  else
95
100
  # UNKNOWN Compressor type!
96
- Gumdrop.report "Unknown javascript compressor type! (#{ opts[:compressor] })", :warning
101
+ @site.report "Unknown javascript compressor type! (#{ opts[:compressor] })", :warning
97
102
  content
98
103
  end
99
104
  end
@@ -104,13 +109,13 @@ module Gumdrop
104
109
  end
105
110
  end
106
111
  if opts[:prune] and opts[:root]
107
- sp = File.expand_path( Gumdrop.config.source_dir )
112
+ sp = File.expand_path( @site.config.source_dir )
108
113
  rp = File.expand_path(opts[:root])
109
114
  relative_root = rp.gsub(sp, '')[1..-1]
110
115
  rrlen= relative_root.length - 1
111
- Gumdrop.site.keys.each do |path|
116
+ @site.node_tree.keys.each do |path|
112
117
  if path[0..rrlen] == relative_root and name != path
113
- Gumdrop.site.delete path
118
+ @site.node_tree.delete path
114
119
  end
115
120
  end
116
121
  end
@@ -131,14 +136,14 @@ module Gumdrop
131
136
  class GeneratedContent < Content
132
137
  # Nothing special, per se...
133
138
 
134
- def initialize(path, block, params={})
139
+ def initialize(path, block, site, params={})
135
140
  @content_block= block
136
- super(path, params)
141
+ super(path, site, params)
137
142
  end
138
143
 
139
- def render(ignore_layout=false, reset_context=true, locals={})
144
+ def render(context=nil, ignore_layout=false, reset_context=true, locals={})
140
145
  if @content_block.nil?
141
- super(ignore_layout, reset_context, locals)
146
+ super(context, ignore_layout, reset_context, locals)
142
147
  else
143
148
  @content_block.call
144
149
  end
@@ -1,4 +1,4 @@
1
- # Rework this to be nicer.. Extend Sintra::Base
1
+ # Rework this to be nicer.. Extend Sintra::Base?
2
2
 
3
3
  require 'sinatra/base'
4
4
  require 'logger'
@@ -6,49 +6,68 @@ require 'logger'
6
6
  module Gumdrop
7
7
 
8
8
  class Server < Sinatra::Base
9
+ site_file= Gumdrop.fetch_site_file
10
+ unless site_file.nil?
11
+ site= Site.new site_file
12
+ site.scan()
9
13
 
10
- set :port, Gumdrop.config.port if Gumdrop.config.port
14
+ set :port, site.config.server_port if site.config.server_port
15
+
16
+ if site.config.proxy_enabled
17
+ require 'gumdrop/proxy_handler'
18
+ get '/-proxy/*' do handle_proxy(params, env) end
19
+ post '/-proxy/*' do handle_proxy(params, env) end
20
+ put '/-proxy/*' do handle_proxy(params, env) end
21
+ delete '/-proxy/*' do handle_proxy(params, env) end
22
+ patch '/-proxy/*' do handle_proxy(params, env) end
23
+ options '/-proxy/*' do handle_proxy(params, env) end
24
+ site.report 'Enabled proxy at /-proxy/*', :info
25
+ end
11
26
 
27
+ get '/*' do
28
+ site.report "- - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
12
29
 
13
- server_log= 'logs/server.log'
14
-
15
- # get '/' do
16
- # redirect '/index.html'
17
- # end
18
-
19
- Gumdrop.run dry_run:true, log:server_log, auto_run:true
20
-
21
- if Gumdrop.config.proxy_enabled
22
- require 'gumdrop/proxy_handler'
23
- Gumdrop.report 'Enabled proxy at /-proxy/*', :info
24
- get '/-proxy/*' do
25
- proxy_to= params[:splat][0]
26
- proxy_parts= proxy_to.split('/')
27
- host= proxy_parts.shift
28
- path_info= "/#{proxy_parts.join('/')}"
29
- #puts "HOST: #{host} PATH_INFO: #{path_info}"
30
- opts={ :to=>host, :path_info=>path_info }
31
- Gumdrop.handle_proxy opts, proxy_to, env
32
- end
33
- post '/-proxy/*' do
34
- proxy_to= params[:splat][0]
35
- proxy_parts= proxy_to.split('/')
36
- host= proxy_parts.shift
37
- path_info= "/#{proxy_parts.join('/')}"
38
- #puts "HOST: #{host} PATH_INFO: #{path_info}"
39
- opts={ :to=>host, :path_info=>path_info }
40
- Gumdrop.handle_proxy opts, proxy_to, env
41
- end
42
- delete '/-proxy/*' do
43
- proxy_to= params[:splat][0]
44
- proxy_parts= proxy_to.split('/')
45
- host= proxy_parts.shift
46
- path_info= "/#{proxy_parts.join('/')}"
47
- #puts "HOST: #{host} PATH_INFO: #{path_info}"
48
- opts={ :to=>host, :path_info=>path_info }
49
- Gumdrop.handle_proxy opts, proxy_to, env
30
+ file_path= get_content_path params[:splat].join('/'), site
31
+ site.report "[#{$$}] GET /#{params[:splat].join('/')} -> #{file_path}"
32
+ since_last_build= Time.now.to_i - site.last_run.to_i
33
+ # site.report "!>!>>>>> since_last_build: #{since_last_build}"
34
+ if since_last_build > site.config.server_timeout
35
+ site.report "[#{$$}] Rebuilding from Source (#{since_last_build} > #{site.config.server_timeout})"
36
+ site.rescan()
37
+ end
38
+
39
+ if site.node_tree.has_key? file_path
40
+ content= site.node_tree[file_path]
41
+ if content.useLayout?
42
+ site.report "[#{$$}] *Dynamic: #{file_path} (#{content.ext})"
43
+ content_type :css if content.ext == '.css' # Meh?
44
+ content_type :js if content.ext == '.js' # Meh?
45
+ content_type :xml if content.ext == '.xml' # Meh?
46
+ content.render
47
+ else
48
+ site.report "[#{$$}] *Static: #{file_path}"
49
+ send_file File.join( site.src_path, file_path)
50
+ end
51
+ else
52
+ site.report "[#{$$}] *Missing: #{file_path}", :error
53
+ "#{file_path} Not Found"
54
+ end
55
+ end
56
+
57
+ def get_content_path(file_path, site)
58
+ keys= [
59
+ file_path,
60
+ "#{file_path}.html",
61
+ "#{file_path}/index.html"
62
+ ]
63
+ if file_path == ""
64
+ "index.html"
65
+ else
66
+ keys.detect {|k| site.node_tree.has_key?(k) }
67
+ end
50
68
  end
51
- put '/-proxy/*' do
69
+
70
+ def handle_proxy(params, env)
52
71
  proxy_to= params[:splat][0]
53
72
  proxy_parts= proxy_to.split('/')
54
73
  host= proxy_parts.shift
@@ -58,76 +77,9 @@ module Gumdrop
58
77
  Gumdrop.handle_proxy opts, proxy_to, env
59
78
  end
60
79
 
61
- end
62
-
63
- get '/*' do
64
- # Gumdrop.log.info "[#{$$}] !>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
65
- file_path= get_content_path params[:splat].join('/')
66
- Gumdrop.log.info "[#{$$}] GET /#{params[:splat].join('/')} -> #{file_path}"
67
- # Gumdrop.log.info " last built: #{Gumdrop.last_run}"
68
- # Gumdrop.log.info "#{Gumdrop.config.inspect}"
69
-
70
- if Gumdrop.site.has_key? file_path
71
- content= Gumdrop.site[file_path]
72
- if content.useLayout?
73
- # Only do a force_reload if the resource is dynamic!
74
- if Gumdrop.config.force_reload
75
- unless %w(.jpg .jpe .jpeg .gif .ico .png).include? File.extname(file_path).to_s
76
- since_last_build= Time.now.to_i - Gumdrop.last_run.to_i
77
- # Gumdrop.log.info "!>!>>>>> since_last_build: #{since_last_build}"
78
- if since_last_build > 10
79
- Gumdrop.log.info "[#{$$}] Rebuilding from Source"
80
- Gumdrop.run dry_run:true, log:server_log
81
- end
82
- end
83
- end
84
- Gumdrop.log.info "[#{$$}] *Dynamic: #{file_path} (#{content.ext})"
85
- content_type :css if content.ext == '.css' # Meh?
86
- content_type :js if content.ext == '.js' # Meh?
87
- content_type :xml if content.ext == '.xml' # Meh?
88
- content.render
89
- else
90
- Gumdrop.log.info "[#{$$}] *Static: #{file_path}"
91
- source_base_path= File.expand_path(Gumdrop.config.source_dir)
92
- send_file File.join( source_base_path, file_path)
93
- end
94
- else
95
- Gumdrop.log.error "[#{$$}] *Missing: #{file_path}"
96
- # Gumdrop.log.info "------------------------"
97
- # Gumdrop.log.info Gumdrop.site.keys.join("\n")
98
- # Gumdrop.log.info "------------------------"
99
- puts "NOT FOUND: #{file_path}"
100
- "#{file_path} Not Found"
101
- end
102
- end
103
-
104
-
105
- def get_content_path(file_path)
106
- keys= [
107
- file_path,
108
- "#{file_path}.html",
109
- "#{file_path}/index.html"
110
- ]
111
- if file_path == ""
112
- "index.html"
113
- else
114
- keys.detect {|k| Gumdrop.site.has_key?(k) }
115
- end
116
- end
117
-
118
- if Gumdrop.config.auto_run
119
- #Gumdrop.run dry_run:true
120
80
  run!
121
- end
122
-
123
- def self.start(opts={})
124
- # Options
125
- opts.reverse_merge! auto_run:true, cache_data:false
126
- Gumdrop.config.merge! opts
127
- Gumdrop.run dry_run:true, log:server_log
128
- ::Gumdrop::Server
81
+ else
82
+ puts "Not in a valid Gumdrop site directory."
129
83
  end
130
-
131
84
  end
132
-
133
- end
85
+ end
@@ -0,0 +1,295 @@
1
+ require 'pathname'
2
+
3
+ # WORK IN PROGRESS!
4
+
5
+ module Gumdrop
6
+
7
+ DEFAULT_OPTIONS= {
8
+ relative_paths: true,
9
+ proxy_enabled: true,
10
+ output_dir: "./output",
11
+ source_dir: "./source",
12
+ data_dir: './data',
13
+ log: './logs/build.log',
14
+ ignore: %w(.DS_Store .gitignore .git .svn .sass-cache),
15
+ server_timeout: 15,
16
+ # server_port: 4567
17
+ }
18
+
19
+ LOG_LEVELS = {
20
+ info: 0,
21
+ warning: 1,
22
+ error: 2
23
+ }
24
+
25
+ class Site
26
+
27
+ attr_reader :opts,
28
+ :root_path,
29
+ :root_path_parts,
30
+ :src_path,
31
+ :src_path_parts,
32
+ :blacklist,
33
+ :greylist,
34
+ :redirects,
35
+ :content_filters,
36
+ :layouts,
37
+ :partials,
38
+ :generators,
39
+ :config,
40
+ :data,
41
+ :sitefile,
42
+ :node_tree,
43
+ :last_run
44
+
45
+
46
+ def initialize(sitefile, opts={})
47
+ @sitefile = File.expand_path sitefile
48
+ @root_path = File.dirname @sitefile
49
+ @root_path_parts = @root_path.split('/')
50
+ @opts = opts
51
+ reset_all()
52
+ end
53
+
54
+ def contents(pattern=nil, opts={})
55
+ if pattern.nil?
56
+ if opts[:as] == :hash
57
+ @node_tree
58
+ else
59
+ @node_tree.values
60
+ end
61
+ else
62
+ nodes = opts[:as] == :hash ? {} : []
63
+ @node_tree.keys.each do |path|
64
+ if path_match path, pattern
65
+ if opts[:as] == :hash
66
+ nodes[path]= @node_tree[path]
67
+ else
68
+ nodes << @node_tree[path]
69
+ end
70
+ end
71
+ end
72
+ nodes
73
+ end
74
+ end
75
+
76
+ def scan
77
+ build_tree()
78
+ run_generators()
79
+ @last_run= Time.now
80
+ self
81
+ end
82
+
83
+ def rescan
84
+ reset_all()
85
+ scan()
86
+ @last_run= Time.now
87
+ self
88
+ end
89
+
90
+ def build
91
+ scan()
92
+ render()
93
+ @last_run= Time.now
94
+ self
95
+ end
96
+
97
+ def report(msg, level=:info)
98
+ # ll= @config.log_level
99
+ case level
100
+ when :info
101
+ @log.info msg
102
+ when :warning
103
+ @log.warn msg
104
+ else
105
+ puts msg
106
+ @log.error msg
107
+ end
108
+ end
109
+
110
+ # FIXME: Should a new Context be created for every page? For now
111
+ # it's a single context for whole site
112
+ def render_context
113
+ @context ||= Context.new self
114
+ @context
115
+ end
116
+
117
+
118
+ private
119
+
120
+ def reset_all
121
+ @content_filters = []
122
+ @blacklist = []
123
+ @greylist = []
124
+ @redirects = []
125
+ @last_run = nil
126
+ @node_tree = Hash.new {|h,k| h[k]= nil }
127
+ @layouts = Hash.new {|h,k| h[k]= nil }
128
+ @partials = Hash.new {|h,k| h[k]= nil }
129
+ @generators = Hash.new {|h,k| h[k]= nil }
130
+ @config = Gumdrop::Config.new DEFAULT_OPTIONS
131
+
132
+ load_sitefile()
133
+
134
+ @data_path = get_expanded_path(@config.data_dir)
135
+ @data = Gumdrop::DataManager.new self, @data_path
136
+ @src_path = get_expanded_path(@config.source_dir)
137
+ @src_path_parts = @src_path.split('/')
138
+ @out_path = get_expanded_path(@config.output_dir)
139
+
140
+ init_logging()
141
+ end
142
+
143
+ def init_logging
144
+ begin
145
+ @log = Logger.new @config.log, 'daily'
146
+ rescue
147
+ @log = Logger.new STDOUT
148
+ end
149
+ @log.formatter = proc do |severity, datetime, progname, msg|
150
+ "#{datetime}: #{msg}\n"
151
+ end
152
+ end
153
+
154
+ def get_expanded_path(path)
155
+ if (Pathname.new path).absolute?
156
+ path
157
+ else
158
+ File.expand_path File.join(@root_path, path)
159
+ end
160
+ end
161
+
162
+ def load_sitefile
163
+ source= IO.readlines( @sitefile ).join('')
164
+ dsl = SitefileDSL.new self
165
+ dsl.instance_eval source
166
+ dsl
167
+ end
168
+
169
+ def build_tree
170
+ report "[Scanning from #{src_path}]", :info
171
+ # Report blacklists and greylists
172
+ blacklist.each do |path|
173
+ report " blacklist: #{path}", :info
174
+ end
175
+ greylist.each do |path|
176
+ report " greylist: #{path}", :info
177
+ end
178
+
179
+ # Scan Filesystem
180
+ #puts "Running in: #{root}"
181
+ Dir.glob("#{src_path}/**/*", File::FNM_DOTMATCH).each do |path|
182
+ unless File.directory? path or @config.ignore.include?( File.basename(path) )
183
+ file_path = (path.split('/') - @root_path_parts).join '/'
184
+ node= Content.new(file_path, self)
185
+ path= node.to_s
186
+ if blacklist.any? {|pattern| path_match path, pattern }
187
+ report "-excluding: #{path}", :info
188
+ else
189
+ node.ignored= greylist.any? {|pattern| path_match path, pattern }
190
+
191
+ # Sort out Layouts, Generators, and Partials
192
+ if File.extname(path) == ".template"
193
+ layouts[path]= node
194
+ layouts[File.basename(path)]= node
195
+
196
+ elsif File.extname(path) == ".generator"
197
+ generators[File.basename(path)]= Generator.new( node, self )
198
+
199
+ elsif File.basename(path).starts_with?("_")
200
+ partial_name= File.basename(path)[1..-1].gsub(File.extname(File.basename(path)), '')
201
+ partial_node_path= File.join File.dirname(path), partial_name
202
+ # puts "Creating partial #{partial_name} from #{path}"
203
+ partials[partial_name]= node
204
+ partials[partial_node_path]= node
205
+ else
206
+ @node_tree[path]= node
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ end
213
+
214
+ def run_generators
215
+ report "[Executing Generators]", :info
216
+ generators.each_pair do |path, generator|
217
+ generator.execute()
218
+ end
219
+ end
220
+
221
+ def render
222
+ unless opts[:dry_run]
223
+ report "[Compiling to #{@out_path}]", :info
224
+ @node_tree.keys.sort.each do |path|
225
+ node= @node_tree[path]
226
+ unless node.ignored #greylist.any? {|pattern| path_match path, pattern }
227
+ # node= @node_tree[path]
228
+ output_path= File.join(@out_path, node.to_s)
229
+ FileUtils.mkdir_p File.dirname(output_path)
230
+ node.renderTo render_context, output_path, content_filters
231
+ else
232
+ report " -ignoring: #{node.to_s}", :info
233
+ end
234
+ end
235
+ end
236
+ end
237
+
238
+ # Match a path using a glob-like file pattern
239
+ def path_match(path, pattern)
240
+ File.fnmatch pattern, path, File::FNM_PATHNAME | File::FNM_DOTMATCH | File::FNM_CASEFOLD
241
+ end
242
+ end
243
+
244
+ class SitefileDSL
245
+
246
+ def initialize(site)
247
+ @site= site
248
+ end
249
+
250
+ def generate(&block)
251
+ # Auto-generated, numerical, key for a site-level generator
252
+ @site.generators[@site.generators.keys.length] = Generator.new(block, @site)
253
+ end
254
+
255
+ def content_filter(&block)
256
+ @site.content_filters << block
257
+ end
258
+
259
+ def skip(path)
260
+ @site.blacklist << path
261
+ end
262
+ alias_method :blacklist, :skip
263
+
264
+ def ignore(path)
265
+ @site.greylist << path
266
+ end
267
+ alias_method :greylist, :ignore
268
+ alias_method :graylist, :ignore
269
+
270
+ def view_helpers(&block)
271
+ Gumdrop::ViewHelpers.class_eval &block
272
+ end
273
+
274
+ def configure(&block)
275
+ if block.arity > 0
276
+ block.call @site.config
277
+ else
278
+ @site.config.instance_eval &block
279
+ end
280
+ end
281
+
282
+ end
283
+
284
+ class Config < HashObject
285
+
286
+ def set(key, value)
287
+ self[key]= value
288
+ end
289
+ def get(key)
290
+ self[key]
291
+ end
292
+
293
+ end
294
+
295
+ end
@@ -1,5 +1,5 @@
1
1
  module Gumdrop
2
2
 
3
- VERSION = "0.5.2" unless defined?(::Gumdrop::VERSION)
3
+ VERSION = "0.6.0" unless defined?(::Gumdrop::VERSION)
4
4
 
5
5
  end
@@ -22,4 +22,4 @@ module Gumdrop
22
22
 
23
23
  end
24
24
 
25
- end
25
+ end