gumdrop 0.7.5 → 0.8.0
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/ChangeLog.md +10 -0
- data/Gemfile.lock +35 -0
- data/bin/gumdrop +16 -7
- data/gumdrop.gemspec +2 -1
- data/lib/gumdrop.rb +38 -13
- data/lib/gumdrop/cli/external.rb +55 -0
- data/lib/gumdrop/cli/internal.rb +75 -0
- data/lib/gumdrop/content.rb +13 -7
- data/lib/gumdrop/generator.rb +16 -85
- data/lib/gumdrop/server.rb +5 -5
- data/lib/gumdrop/site.rb +94 -28
- data/lib/gumdrop/support/base_packager.rb +60 -0
- data/lib/gumdrop/{callbacks.rb → support/callbacks.rb} +1 -1
- data/lib/gumdrop/{hash_object.rb → support/hash_object.rb} +0 -0
- data/lib/gumdrop/{proxy_handler.rb → support/proxy_handler.rb} +0 -0
- data/lib/gumdrop/support/sprockets.rb +34 -0
- data/lib/gumdrop/support/stitch.rb +144 -0
- data/lib/gumdrop/version.rb +1 -1
- data/notes.md +347 -0
- data/templates/backbone/Gemfile +1 -1
- data/templates/backbone/Gumdrop +6 -1
- data/templates/blank/Gemfile +2 -2
- data/templates/blank/Gumdrop +25 -2
- data/templates/default/Gemfile +2 -2
- data/templates/default/Gumdrop +27 -1
- data/templates/default/Rakefile +15 -15
- metadata +30 -14
- data/lib/gumdrop/cli.rb +0 -167
- data/lib/gumdrop/sprockets_support.rb +0 -3
- data/lib/gumdrop/stitch_support.rb +0 -114
- data/templates/blank/Rakefile +0 -38
data/lib/gumdrop/version.rb
CHANGED
data/notes.md
ADDED
@@ -0,0 +1,347 @@
|
|
1
|
+
# General
|
2
|
+
|
3
|
+
Gumdrop is based on personal and project code from several years ago. It's been slowly updated and improved over time. But I've not been able to give it a lot of sustained effort to get it where I want it.
|
4
|
+
|
5
|
+
Since I started, Jekyl, MiddleMan, and other static site tools have been released. While they are all good in their own ways, each miss something that I want. The main difference, at this point in time, are generators. I want to have a static site, but still have data-driven pages.
|
6
|
+
|
7
|
+
Anyway, this is just a place for me to put down my thoughts on what I want to do to get it servicable as a 1.0 candidate:
|
8
|
+
|
9
|
+
|
10
|
+
# Architectural Changes
|
11
|
+
|
12
|
+
- Rendering should be abstracted from Content objects
|
13
|
+
- Building should be abstracted from Site object
|
14
|
+
- Generators should be able to unload themselves and any pages they created
|
15
|
+
- There should be discreet ways of knowing whether a Content object is renderable or not (other than file ext)
|
16
|
+
- DSL generators should accept a name, and optional base_path (applied to all generated page paths)
|
17
|
+
- Callbacks should have the site var in scope
|
18
|
+
- Reporting/logging needs to be abstracted from Site and all dependant objects (Loggable module?)
|
19
|
+
- Project Templates should be abstracted from CLI::Internal (and External) into a module
|
20
|
+
- Make `DataManager` more OO -- abstract data providers. Each provider would specifiy the file extensions they handle.
|
21
|
+
- Bundler.require site.config.env.to_sym
|
22
|
+
|
23
|
+
## Nice to Haves
|
24
|
+
|
25
|
+
- Server should be able to selectively render individual content without rescanning/building the whole tree
|
26
|
+
|
27
|
+
# Impl Notes
|
28
|
+
|
29
|
+
## Content
|
30
|
+
|
31
|
+
- Content objects can know their `level` based on their `full_path` (basically count the slashes)
|
32
|
+
|
33
|
+
```rb
|
34
|
+
contentA = Content.new "source/page.html.erb"
|
35
|
+
contentB = Content.new "source/logo.png"
|
36
|
+
|
37
|
+
contentA.binary? #=> false
|
38
|
+
contentB.binary? #=> true
|
39
|
+
```
|
40
|
+
|
41
|
+
Possible `binary?` method (from ptools):
|
42
|
+
|
43
|
+
```rb
|
44
|
+
class Content
|
45
|
+
def initialize(source_path, generator=nil, &block)
|
46
|
+
@source_path= source_path
|
47
|
+
@filename= File.basename(source_path)
|
48
|
+
@generator=generator
|
49
|
+
@block= block
|
50
|
+
# etc...
|
51
|
+
end
|
52
|
+
|
53
|
+
def binary?(file)
|
54
|
+
@is_binary ||= begin
|
55
|
+
s = (File.read(file, File.stat(file).blksize) || "").split(//)
|
56
|
+
((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def generated?
|
61
|
+
!@generator.nil?
|
62
|
+
emd
|
63
|
+
|
64
|
+
def body # Memoize this?
|
65
|
+
if @block
|
66
|
+
@block.call
|
67
|
+
else
|
68
|
+
File.read @source_path
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
## Events/Observable
|
75
|
+
|
76
|
+
Use Observable internally?
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
module Eventable
|
80
|
+
include Observable
|
81
|
+
|
82
|
+
def fire(action, data={}, sender=self)
|
83
|
+
changed
|
84
|
+
notify_observers(sender, action, data)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Usage
|
89
|
+
class MyClass
|
90
|
+
includes Eventable
|
91
|
+
|
92
|
+
def execute
|
93
|
+
fire :my_action, ex:'data'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
```
|
98
|
+
|
99
|
+
Listeners would always have the same signature:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
class Listener
|
103
|
+
def update(sender, action, data)
|
104
|
+
puts "(#{ sender.inspect }) #{action}: #{ data.inspect }"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
```
|
108
|
+
Have the site listen and bubble the events?
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
class Site
|
112
|
+
includes Eventable
|
113
|
+
|
114
|
+
def # wherever
|
115
|
+
renderer= HtmlRenderer.new
|
116
|
+
renderer.add_observer self
|
117
|
+
renderer.draw # whatever
|
118
|
+
|
119
|
+
#done
|
120
|
+
renderer.delete_observers
|
121
|
+
end
|
122
|
+
|
123
|
+
def update(sender, action, data)
|
124
|
+
fire action, data, sender
|
125
|
+
end
|
126
|
+
end
|
127
|
+
```
|
128
|
+
|
129
|
+
## Rendering
|
130
|
+
|
131
|
+
```rb
|
132
|
+
|
133
|
+
class RenderContext
|
134
|
+
include ViewHelpers
|
135
|
+
|
136
|
+
attr_reader :content, :site, :state
|
137
|
+
# attr_writer :content, :renderer
|
138
|
+
|
139
|
+
def initialize(content, renderer, parent_context=nil)
|
140
|
+
@content= content
|
141
|
+
@renderer= renderer
|
142
|
+
@site= renderer.site
|
143
|
+
@state= {}
|
144
|
+
@parent= parent_context
|
145
|
+
end
|
146
|
+
|
147
|
+
def render(*args) # Not exactly sure yet
|
148
|
+
content= # extract/lookup a content object from args
|
149
|
+
# maybe allow relative lookups from content?
|
150
|
+
@renderer.draw_partial content
|
151
|
+
end
|
152
|
+
|
153
|
+
def get(key)
|
154
|
+
@state[key]
|
155
|
+
end
|
156
|
+
|
157
|
+
def set(key, value)
|
158
|
+
@state[key]= value
|
159
|
+
end
|
160
|
+
|
161
|
+
def method_missing(*stuff)
|
162
|
+
# try to get/set from @state
|
163
|
+
# else try to get from @parent unless @parent.nil?
|
164
|
+
# else try and get it from @content
|
165
|
+
# else return nil ???
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class Renderer
|
170
|
+
includes Eventable
|
171
|
+
|
172
|
+
attr_reader :site, :context
|
173
|
+
|
174
|
+
def initialize(site)
|
175
|
+
@site= site
|
176
|
+
end
|
177
|
+
|
178
|
+
def draw(content, opts={})
|
179
|
+
return nil if content.binary?
|
180
|
+
@context= RenderContext.new site, content, self
|
181
|
+
output= _render_content content, opts
|
182
|
+
fire :render, content:content, output:output
|
183
|
+
output
|
184
|
+
end
|
185
|
+
|
186
|
+
def draw_partial(content, opts={})
|
187
|
+
return nil if content.binary?
|
188
|
+
opts.defaults! no_layout:true
|
189
|
+
_sub_context content
|
190
|
+
output= _render_content content, opts
|
191
|
+
fire :render_partial, content:content, output:output
|
192
|
+
_revert_context
|
193
|
+
output
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
def _render_content(content, opts)
|
199
|
+
output= content.body
|
200
|
+
_render_pipeline(content.filename).each do |template|
|
201
|
+
output= _render_text output, template
|
202
|
+
end
|
203
|
+
output- _render_layout output unless opts[:no_layout]
|
204
|
+
output
|
205
|
+
end
|
206
|
+
|
207
|
+
def _render_text(text, tmpl, opts)
|
208
|
+
# Tilt Code
|
209
|
+
tmpl.new(opts).render(text) # pass opts on through?
|
210
|
+
end
|
211
|
+
|
212
|
+
def _render_layout(text)
|
213
|
+
# Look for @context[:layout] or site.config.default_template
|
214
|
+
end
|
215
|
+
|
216
|
+
def _render_pipeline(path)
|
217
|
+
# path.split('.') # then sort out how to:
|
218
|
+
# return an array of Tilt templates based on file exts
|
219
|
+
[]
|
220
|
+
end
|
221
|
+
|
222
|
+
def _sub_context(content)
|
223
|
+
@old_context= @context
|
224
|
+
@context= Context.new site, content, self, @old_context
|
225
|
+
end
|
226
|
+
|
227
|
+
def _revert_context
|
228
|
+
@context = @old_context
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
```
|
234
|
+
|
235
|
+
## Building
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
class Builder
|
239
|
+
include Eventable
|
240
|
+
|
241
|
+
attr_accessor :site, :renderer
|
242
|
+
|
243
|
+
def initialize(site=nil, renderer=nil)
|
244
|
+
@site= site
|
245
|
+
@renderer= renderer.nil? ? Renderer.new(site) : renderer
|
246
|
+
end
|
247
|
+
|
248
|
+
def execute()
|
249
|
+
fire :build_start
|
250
|
+
site.contents.each do |content|
|
251
|
+
if content.binary?
|
252
|
+
_copy content.src_path => _output_path(content)
|
253
|
+
else
|
254
|
+
output = renderer.draw content
|
255
|
+
_write output => _output_path(content)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
fire :build_end
|
259
|
+
rescue => ex
|
260
|
+
fire :exception, source:self, ex:ex
|
261
|
+
end
|
262
|
+
|
263
|
+
private
|
264
|
+
|
265
|
+
def _copy(files)
|
266
|
+
files.each_pair do |from,to|
|
267
|
+
# File.write code
|
268
|
+
fire :copy, from:from, to:to
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def _write(files)
|
273
|
+
files.each_pair do |text,to|
|
274
|
+
# File.write code
|
275
|
+
fire :write, from:from, to:to
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
# Usage
|
282
|
+
site= Gumdrop.local_site #=> Site
|
283
|
+
renderer= HtmlRenderer.new
|
284
|
+
builder= Builder.new site, renderer
|
285
|
+
builder.execute()
|
286
|
+
```
|
287
|
+
|
288
|
+
# Logging via Events?
|
289
|
+
|
290
|
+
- Only two levels? `:info` and `:warn`? Or add `:error`
|
291
|
+
|
292
|
+
```ruby
|
293
|
+
# Mixin to your class:
|
294
|
+
module Loggable
|
295
|
+
|
296
|
+
# Assumes Eventable? or:
|
297
|
+
# include Eventable
|
298
|
+
|
299
|
+
def log(level, msg=nil)
|
300
|
+
if msg.nil?
|
301
|
+
msg= level
|
302
|
+
level= :info
|
303
|
+
end
|
304
|
+
fire :log, level:level, message:msg
|
305
|
+
end
|
306
|
+
|
307
|
+
def warn(msg)
|
308
|
+
log :warn, msg
|
309
|
+
end
|
310
|
+
|
311
|
+
end
|
312
|
+
|
313
|
+
# Loggers
|
314
|
+
class ConsoleLogger
|
315
|
+
def initialize(levels, format='%<level>s : %<message>s')
|
316
|
+
@listen_for= levels
|
317
|
+
@format= format
|
318
|
+
end
|
319
|
+
def update(sender, action, data)
|
320
|
+
if action == :log
|
321
|
+
if @listen_for.include? data.level
|
322
|
+
data[:sender]= sender.to_s
|
323
|
+
data[:time]= Time.new
|
324
|
+
puts sprintf(@format, data)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
```
|
330
|
+
|
331
|
+
Or maybe not... I think maybe this:
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
module Loggable
|
335
|
+
def log
|
336
|
+
Gumdrop.logger
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
module Gumdrop
|
341
|
+
class << self
|
342
|
+
attr_accessor :logger
|
343
|
+
end
|
344
|
+
end
|
345
|
+
```
|
346
|
+
|
347
|
+
|
data/templates/backbone/Gemfile
CHANGED
data/templates/backbone/Gumdrop
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
puts "Gumdrop v#{Gumdrop::VERSION} building..."
|
2
1
|
|
3
2
|
configure do
|
4
3
|
|
@@ -80,6 +79,12 @@ generate do
|
|
80
79
|
|
81
80
|
end
|
82
81
|
|
82
|
+
# All Gumdrop Hooks: on_start, on_before_scan, on_scan, on_before_generate, on_generate, on_before_render, on_render, on_end
|
83
|
+
on_start do |site|
|
84
|
+
puts "Gumdrop v#{Gumdrop::VERSION} building..."
|
85
|
+
end
|
86
|
+
# on_end do |site| end
|
87
|
+
|
83
88
|
# Example of using a content filter to compress CSS output
|
84
89
|
# require 'yui/compressor'
|
85
90
|
# content_filter do |content, info|
|
data/templates/blank/Gemfile
CHANGED
data/templates/blank/Gumdrop
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
puts "Gumdrop v#{Gumdrop::VERSION} building..."
|
2
1
|
|
3
2
|
configure do
|
4
3
|
|
@@ -14,6 +13,10 @@ configure do
|
|
14
13
|
# set :server_timeout, 15
|
15
14
|
# set :server_port, 4567
|
16
15
|
|
16
|
+
# set :server, "REMOTE_USERNAME"
|
17
|
+
# set :server_user, 'server.com'
|
18
|
+
# set :server_path, '~/server.com'
|
19
|
+
|
17
20
|
end
|
18
21
|
|
19
22
|
|
@@ -89,7 +92,9 @@ end
|
|
89
92
|
# end
|
90
93
|
|
91
94
|
# All Gumdrop Hooks: on_start, on_before_scan, on_scan, on_before_generate, on_generate, on_before_render, on_render, on_end
|
92
|
-
|
95
|
+
on_start do |site|
|
96
|
+
puts "Gumdrop v#{Gumdrop::VERSION} building..."
|
97
|
+
end
|
93
98
|
# on_end do |site| end
|
94
99
|
|
95
100
|
|
@@ -112,6 +117,24 @@ view_helpers do
|
|
112
117
|
|
113
118
|
end
|
114
119
|
|
120
|
+
# Add (thor) tasks to gumdrop command
|
121
|
+
# for more: https://github.com/wycats/thor/wiki
|
122
|
+
# tasks do
|
123
|
+
|
124
|
+
# desc 'sync', "Syncs with public server using rsync (if configured)"
|
125
|
+
# method_option :build, :aliases => '-b', :desc => 'Build content before syncing'
|
126
|
+
# method_option :dry_run, :aliases => '-d', :desc => 'Dry run'
|
127
|
+
# def sync
|
128
|
+
# SITE.build if options[:build]
|
129
|
+
# config= SITE.config
|
130
|
+
# output= SITE.config.output_dir
|
131
|
+
# cmd= "rsync -avz --delete #{output} #{config.server_user}@#{config.server}:#{config.server_path}"
|
132
|
+
# puts "Running:\n#{cmd}\n"
|
133
|
+
# system(cmd) unless options[:dry_run]
|
134
|
+
# end
|
135
|
+
|
136
|
+
# end
|
137
|
+
|
115
138
|
# Any specialized code for your site goes here...
|
116
139
|
|
117
140
|
# require 'slim'
|