spontaneous 0.2.0.beta4 → 0.2.0.beta5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -0
- data/Gemfile +11 -6
- data/Readme.markdown +136 -69
- data/application/css/core.css.scss +27 -7
- data/application/css/editing.css.scss +4 -26
- data/application/css/schema_error.css.scss +22 -0
- data/application/js/content.js +11 -3
- data/application/js/edit_panel.js +1 -4
- data/application/js/field/file.js +17 -0
- data/application/js/field/image.js +30 -21
- data/application/js/field/string.js +4 -1
- data/application/js/field_preview.js +21 -16
- data/application/js/publish.js +6 -6
- data/application/js/types.js +5 -13
- data/application/js/views.js +2 -2
- data/application/js/views/box_view.js +3 -2
- data/application/js/views/page_piece_view.js +1 -1
- data/application/js/views/piece_view.js +1 -1
- data/application/views/schema_modification_error.html.erb +13 -3
- data/db/migrations/20131104101935_site_must_publish_all.rb +14 -0
- data/lib/spontaneous.rb +0 -1
- data/lib/spontaneous/box_style.rb +15 -9
- data/lib/spontaneous/capistrano/deploy.rb +13 -1
- data/lib/spontaneous/change.rb +11 -13
- data/lib/spontaneous/cli.rb +5 -2
- data/lib/spontaneous/cli/assets.rb +7 -1
- data/lib/spontaneous/cli/console.rb +7 -1
- data/lib/spontaneous/cli/content.rb +35 -0
- data/lib/spontaneous/cli/fields.rb +3 -2
- data/lib/spontaneous/cli/generate.rb +5 -2
- data/lib/spontaneous/cli/server.rb +12 -8
- data/lib/spontaneous/cli/site.rb +12 -12
- data/lib/spontaneous/cli/user.rb +28 -14
- data/lib/spontaneous/collections/box_set.rb +4 -4
- data/lib/spontaneous/collections/field_set.rb +4 -4
- data/lib/spontaneous/collections/prototype_set.rb +12 -4
- data/lib/spontaneous/data_mapper.rb +11 -7
- data/lib/spontaneous/data_mapper/content_model.rb +8 -0
- data/lib/spontaneous/data_mapper/content_model/associations.rb +1 -1
- data/lib/spontaneous/data_mapper/dataset.rb +14 -2
- data/lib/spontaneous/data_mapper/scope.rb +33 -13
- data/lib/spontaneous/facet.rb +4 -0
- data/lib/spontaneous/field.rb +12 -12
- data/lib/spontaneous/field/base.rb +27 -22
- data/lib/spontaneous/field/boolean.rb +4 -4
- data/lib/spontaneous/field/date.rb +2 -2
- data/lib/spontaneous/field/file.rb +24 -18
- data/lib/spontaneous/field/html.rb +1 -1
- data/lib/spontaneous/field/image.rb +6 -19
- data/lib/spontaneous/field/location.rb +1 -1
- data/lib/spontaneous/field/long_string.rb +3 -3
- data/lib/spontaneous/field/markdown.rb +3 -3
- data/lib/spontaneous/field/select.rb +2 -2
- data/lib/spontaneous/field/string.rb +2 -2
- data/lib/spontaneous/field/tags.rb +2 -2
- data/lib/spontaneous/field/update.rb +21 -20
- data/lib/spontaneous/field/webvideo.rb +6 -6
- data/lib/spontaneous/field/webvideo/fallback.rb +2 -2
- data/lib/spontaneous/field/webvideo/vimeo.rb +7 -7
- data/lib/spontaneous/generators/site.rb +2 -2
- data/lib/spontaneous/generators/site/Gemfile.tt +5 -1
- data/lib/spontaneous/layout.rb +2 -2
- data/lib/spontaneous/media.rb +1 -0
- data/lib/spontaneous/media/file.rb +6 -5
- data/lib/spontaneous/media/image/attributes.rb +4 -0
- data/lib/spontaneous/media/image/renderable.rb +4 -4
- data/lib/spontaneous/media/store.rb +22 -0
- data/lib/spontaneous/{storage → media/store}/backend.rb +1 -1
- data/lib/spontaneous/{storage → media/store}/cloud.rb +1 -1
- data/lib/spontaneous/{storage → media/store}/local.rb +1 -1
- data/lib/spontaneous/media/temp_file.rb +1 -1
- data/lib/spontaneous/model.rb +10 -7
- data/lib/spontaneous/model/action.rb +7 -0
- data/lib/spontaneous/model/action/clean.rb +87 -0
- data/lib/spontaneous/model/box/allowed_types.rb +15 -1
- data/lib/spontaneous/model/core.rb +10 -0
- data/lib/spontaneous/model/core/aliases.rb +1 -1
- data/lib/spontaneous/model/core/content_groups.rb +1 -1
- data/lib/spontaneous/model/core/fields.rb +1 -1
- data/lib/spontaneous/model/core/modifications.rb +2 -2
- data/lib/spontaneous/model/core/page_search.rb +4 -0
- data/lib/spontaneous/model/core/publishing.rb +4 -17
- data/lib/spontaneous/model/core/render.rb +4 -4
- data/lib/spontaneous/model/core/styles.rb +2 -2
- data/lib/spontaneous/model/core/visibility.rb +6 -2
- data/lib/spontaneous/model/page.rb +6 -2
- data/lib/spontaneous/model/page/controllers.rb +55 -17
- data/lib/spontaneous/model/page/formats.rb +12 -7
- data/lib/spontaneous/model/page/layouts.rb +2 -2
- data/lib/spontaneous/model/page/locks.rb +4 -1
- data/lib/spontaneous/model/page/page_tree.rb +40 -6
- data/lib/spontaneous/output.rb +14 -52
- data/lib/spontaneous/output/context.rb +11 -39
- data/lib/spontaneous/output/context/navigation.rb +31 -0
- data/lib/spontaneous/output/format.rb +15 -19
- data/lib/spontaneous/output/renderable.rb +99 -0
- data/lib/spontaneous/output/store.rb +24 -0
- data/lib/spontaneous/output/store/backend.rb +52 -0
- data/lib/spontaneous/output/store/file.rb +77 -0
- data/lib/spontaneous/output/store/moneta.rb +117 -0
- data/lib/spontaneous/output/store/revision.rb +34 -0
- data/lib/spontaneous/output/store/store.rb +15 -0
- data/lib/spontaneous/output/store/transaction.rb +44 -0
- data/lib/spontaneous/output/template/engine.rb +17 -7
- data/lib/spontaneous/output/template/renderer.rb +66 -40
- data/lib/spontaneous/page_lock.rb +5 -7
- data/lib/spontaneous/page_piece.rb +2 -2
- data/lib/spontaneous/permissions/user.rb +14 -7
- data/lib/spontaneous/plugins/application/features.rb +8 -4
- data/lib/spontaneous/plugins/application/state.rb +12 -6
- data/lib/spontaneous/prototypes/box_prototype.rb +9 -10
- data/lib/spontaneous/prototypes/field_prototype.rb +66 -15
- data/lib/spontaneous/publishing/immediate.rb +30 -26
- data/lib/spontaneous/rack.rb +12 -7
- data/lib/spontaneous/rack/back.rb +43 -37
- data/lib/spontaneous/rack/back/base.rb +4 -4
- data/lib/spontaneous/rack/back/changes.rb +2 -2
- data/lib/spontaneous/rack/back/file.rb +16 -24
- data/lib/spontaneous/rack/back/map.rb +5 -5
- data/lib/spontaneous/rack/back/preview.rb +3 -4
- data/lib/spontaneous/rack/back/schema.rb +1 -1
- data/lib/spontaneous/rack/back/site.rb +6 -7
- data/lib/spontaneous/rack/front.rb +19 -16
- data/lib/spontaneous/rack/middleware/authenticate.rb +3 -3
- data/lib/spontaneous/rack/middleware/reloader.rb +3 -2
- data/lib/spontaneous/rack/middleware/scope.rb +25 -19
- data/lib/spontaneous/rack/page_controller.rb +164 -13
- data/lib/spontaneous/rack/public.rb +23 -62
- data/lib/spontaneous/rack/static.rb +2 -3
- data/lib/spontaneous/schema.rb +27 -8
- data/lib/spontaneous/schema/schema_modification.rb +9 -1
- data/lib/spontaneous/schema/uid.rb +2 -2
- data/lib/spontaneous/schema/uid_map.rb +3 -2
- data/lib/spontaneous/search/database.rb +2 -2
- data/lib/spontaneous/search/field.rb +5 -3
- data/lib/spontaneous/search/index.rb +12 -7
- data/lib/spontaneous/search/results.rb +5 -3
- data/lib/spontaneous/server.rb +2 -2
- data/lib/spontaneous/site.rb +10 -3
- data/lib/spontaneous/site/features.rb +26 -6
- data/lib/spontaneous/site/helpers.rb +9 -12
- data/lib/spontaneous/site/level.rb +7 -9
- data/lib/spontaneous/site/map.rb +9 -11
- data/lib/spontaneous/site/paths.rb +5 -5
- data/lib/spontaneous/site/publishing.rb +83 -80
- data/lib/spontaneous/site/schema.rb +1 -7
- data/lib/spontaneous/site/search.rb +8 -18
- data/lib/spontaneous/site/selectors.rb +60 -54
- data/lib/spontaneous/site/state.rb +36 -30
- data/lib/spontaneous/site/storage.rb +10 -16
- data/lib/spontaneous/state.rb +8 -0
- data/lib/spontaneous/style.rb +32 -33
- data/lib/spontaneous/version.rb +1 -1
- data/spontaneous.gemspec +22 -21
- data/test/fixtures/public/templates/layouts/default.html.cut +1 -1
- data/test/fixtures/public/templates/layouts/default.pdf.cut +1 -1
- data/test/fixtures/public/templates/layouts/default.rss.cut +1 -1
- data/test/fixtures/search/config/indexes.rb +1 -1
- data/test/fixtures/serialisation/class_hash.yaml.erb +13 -1
- data/test/fixtures/serialisation/root_hash.yaml.erb +10 -0
- data/test/functional/test_application.rb +20 -24
- data/test/functional/test_back.rb +26 -27
- data/test/functional/test_cli.rb +146 -0
- data/test/functional/test_front.rb +287 -216
- data/test/functional/test_user_manager.rb +1 -1
- data/test/test_helper.rb +15 -11
- data/test/unit/test_alias.rb +32 -25
- data/test/unit/test_asset_bundler.rb +1 -1
- data/test/unit/test_assets.rb +34 -33
- data/test/unit/test_authentication.rb +1 -1
- data/test/unit/test_boxes.rb +16 -2
- data/test/unit/test_changesets.rb +23 -11
- data/test/unit/test_content.rb +15 -0
- data/test/unit/test_context.rb +139 -0
- data/test/unit/test_controllers.rb +374 -0
- data/test/{experimental → unit}/test_crypt.rb +0 -0
- data/test/unit/test_datamapper.rb +260 -237
- data/test/unit/test_datamapper_content.rb +42 -12
- data/test/{experimental → unit}/test_features.rb +85 -3
- data/test/unit/test_fields.rb +117 -42
- data/test/unit/test_formats.rb +11 -1
- data/test/unit/test_generators.rb +2 -2
- data/test/unit/test_helpers.rb +7 -8
- data/test/unit/test_images.rb +39 -2
- data/test/unit/test_layouts.rb +14 -12
- data/test/unit/test_media.rb +32 -23
- data/test/unit/test_output_store.rb +342 -0
- data/test/unit/test_page.rb +8 -1
- data/test/unit/test_permissions.rb +11 -7
- data/test/unit/test_plugins.rb +3 -3
- data/test/unit/test_prototype_set.rb +8 -1
- data/test/unit/test_publishing.rb +67 -54
- data/test/unit/test_render.rb +91 -38
- data/test/unit/test_revisions.rb +4 -4
- data/test/unit/test_schema.rb +109 -84
- data/test/unit/test_search.rb +42 -42
- data/test/unit/test_serialisation.rb +3 -2
- data/test/unit/test_site.rb +39 -27
- data/test/unit/test_storage.rb +9 -6
- data/test/unit/test_styles.rb +25 -32
- data/test/unit/test_templates.rb +8 -4
- metadata +89 -54
- data/lib/spontaneous/model/page/request.rb +0 -105
- data/lib/spontaneous/storage.rb +0 -22
@@ -1,32 +1,14 @@
|
|
1
1
|
|
2
2
|
module Spontaneous::Output::Context
|
3
3
|
autoload :RenderCache, 'spontaneous/output/context/render_cache'
|
4
|
+
autoload :Navigation, 'spontaneous/output/context/navigation'
|
4
5
|
|
5
6
|
module ContextCore
|
6
7
|
include RenderCache
|
7
|
-
|
8
|
-
|
9
|
-
def navigation(depth = 1, &block)
|
10
|
-
case depth
|
11
|
-
when 0, :root
|
12
|
-
root
|
13
|
-
when 1, :section
|
14
|
-
navigation_at_depth(1, &block)
|
15
|
-
else
|
16
|
-
navigation_at_depth(depth, &block)
|
17
|
-
end
|
18
|
-
end
|
8
|
+
include Navigation
|
19
9
|
|
20
|
-
|
21
|
-
current_page = __target.page
|
22
|
-
__pages_at_depth(current_page, depth).each do |p|
|
23
|
-
yield(p, current_page.active?(p))
|
24
|
-
end
|
25
|
-
end
|
10
|
+
attr_accessor :_renderer, :site
|
26
11
|
|
27
|
-
def __pages_at_depth(origin_page, depth)
|
28
|
-
origin_page.at_depth(depth)
|
29
|
-
end
|
30
12
|
|
31
13
|
def page
|
32
14
|
__target.page
|
@@ -45,11 +27,11 @@ module Spontaneous::Output::Context
|
|
45
27
|
end
|
46
28
|
|
47
29
|
def root
|
48
|
-
|
30
|
+
site.home
|
49
31
|
end
|
50
32
|
|
51
33
|
def site_page(path)
|
52
|
-
|
34
|
+
site[path]
|
53
35
|
end
|
54
36
|
|
55
37
|
def asset_environment
|
@@ -66,10 +48,6 @@ module Spontaneous::Output::Context
|
|
66
48
|
"url(#{asset_path(path, options)})"
|
67
49
|
end
|
68
50
|
|
69
|
-
def site
|
70
|
-
Spontaneous::Site.instance
|
71
|
-
end
|
72
|
-
|
73
51
|
def publishing?
|
74
52
|
false
|
75
53
|
end
|
@@ -136,15 +114,9 @@ module Spontaneous::Output::Context
|
|
136
114
|
end
|
137
115
|
|
138
116
|
def __decode_params(param)
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
param = param.send(@_render_method)
|
143
|
-
else
|
144
|
-
if param.respond_to?(:render)
|
145
|
-
param = __render_content(param) #render(param, param.template)
|
146
|
-
end
|
147
|
-
end
|
117
|
+
return param if param.is_a?(String)
|
118
|
+
if param.respond_to?(:render)
|
119
|
+
param = __render_content(param) #render(param, param.template)
|
148
120
|
end
|
149
121
|
param.to_s
|
150
122
|
end
|
@@ -153,9 +125,9 @@ module Spontaneous::Output::Context
|
|
153
125
|
# use of shared caches that are held by it.
|
154
126
|
def __render_content(content)
|
155
127
|
if content.respond_to?(:render_using)
|
156
|
-
content.render_using(_renderer, __format, self)
|
128
|
+
content.render_using(_renderer, __format, {}, self)
|
157
129
|
else
|
158
|
-
content.render(__format, self)
|
130
|
+
content.render(__format, {}, self)
|
159
131
|
end
|
160
132
|
end
|
161
133
|
end
|
@@ -186,7 +158,7 @@ module Spontaneous::Output::Context
|
|
186
158
|
end
|
187
159
|
end
|
188
160
|
|
189
|
-
def __pages_at_depth(origin_page, depth)
|
161
|
+
def __pages_at_depth(origin_page, depth, opts = {})
|
190
162
|
_with_render_cache("pages_at_depth.#{origin_page.id}.#{depth}") do
|
191
163
|
super
|
192
164
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Spontaneous::Output::Context
|
2
|
+
module Navigation
|
3
|
+
def navigation(opts = {}, &block)
|
4
|
+
depth = opts.delete(:depth) || 1
|
5
|
+
case depth
|
6
|
+
when 0, :root
|
7
|
+
root
|
8
|
+
when 1, :section
|
9
|
+
navigation_at_depth(1, opts, &block)
|
10
|
+
else
|
11
|
+
navigation_at_depth(depth, opts, &block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def navigation_at_depth(depth, opts = {})
|
16
|
+
current_page = __target.page
|
17
|
+
pages = __pages_at_depth(current_page, depth, opts).map { |p| [p, current_page.active?(p)] }
|
18
|
+
if block_given?
|
19
|
+
pages.each do |p, active|
|
20
|
+
yield(p, active)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
pages
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def __pages_at_depth(origin_page, depth, opts = {})
|
28
|
+
origin_page.at_depth(depth, opts)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -65,6 +65,10 @@ module Spontaneous::Output
|
|
65
65
|
@name
|
66
66
|
end
|
67
67
|
|
68
|
+
def to_s
|
69
|
+
to_sym.to_s
|
70
|
+
end
|
71
|
+
|
68
72
|
def extension(is_dynamic = false, dynamic_extension = Spontaneous::Output::Template.extension)
|
69
73
|
if (override = @options[:extension])
|
70
74
|
return normalise_extension(override.to_s)
|
@@ -99,8 +103,8 @@ module Spontaneous::Output
|
|
99
103
|
@options[:postprocess]
|
100
104
|
end
|
101
105
|
|
102
|
-
def context
|
103
|
-
|
106
|
+
def context(site = Spontaneous.instance)
|
107
|
+
site.context (helper_formats + [name]).uniq.compact
|
104
108
|
end
|
105
109
|
end
|
106
110
|
|
@@ -121,20 +125,20 @@ module Spontaneous::Output
|
|
121
125
|
content.model
|
122
126
|
end
|
123
127
|
|
124
|
-
def render(params = {},
|
125
|
-
render_using(default_renderer, params,
|
128
|
+
def render(params = {}, parent_context = nil)
|
129
|
+
render_using(default_renderer, params, parent_context)
|
126
130
|
end
|
127
131
|
|
128
|
-
def render_using(renderer, params,
|
132
|
+
def render_using(renderer, params = {}, parent_context = nil)
|
129
133
|
before_render
|
130
|
-
output = render_page(renderer, params,
|
134
|
+
output = render_page(renderer, params, parent_context)
|
131
135
|
output = postprocess(output)
|
132
136
|
after_render(output)
|
133
137
|
output
|
134
138
|
end
|
135
139
|
|
136
140
|
def default_renderer
|
137
|
-
Spontaneous::Output.
|
141
|
+
Spontaneous::Output.default_renderer
|
138
142
|
end
|
139
143
|
|
140
144
|
def before_render
|
@@ -150,21 +154,13 @@ module Spontaneous::Output
|
|
150
154
|
output
|
151
155
|
end
|
152
156
|
|
153
|
-
def render_page(renderer, params = {},
|
154
|
-
renderer.render(self, params,
|
157
|
+
def render_page(renderer, params = {}, parent_context = nil)
|
158
|
+
renderer.render(self, params, parent_context)
|
155
159
|
end
|
156
160
|
|
157
|
-
def publish_page(renderer, revision)
|
161
|
+
def publish_page(renderer, revision, transaction)
|
158
162
|
rendered = render_using(renderer, {:revision => revision})
|
159
|
-
|
160
|
-
File.open(path, 'w') do |file|
|
161
|
-
case rendered
|
162
|
-
when IO
|
163
|
-
IO.copy_stream(rendered, file)
|
164
|
-
else
|
165
|
-
file.write(rendered.to_s)
|
166
|
-
end
|
167
|
-
end
|
163
|
+
transaction.store(self, renderer.is_dynamic_template?(rendered), rendered)
|
168
164
|
end
|
169
165
|
|
170
166
|
def output_path(revision, render)
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# The Renderable class is wrapped around the targets of the render context in
|
2
|
+
# order to solve a particular problem around passing parameters/locals to
|
3
|
+
# field & content render calls from within templates.
|
4
|
+
|
5
|
+
# Say you wanted to render an image field but pass it some size parameters
|
6
|
+
# in order to insert it at a particular size. You would do this in the template
|
7
|
+
# like this:
|
8
|
+
#
|
9
|
+
# ${ image.thumbnail(width: 50, height: 50) }
|
10
|
+
#
|
11
|
+
# this renders the image into the template using the following call stack:
|
12
|
+
#
|
13
|
+
# context.__decode_params(context.image.thumbnail(width: 50, height: 50)).render(__format, {}, ...)
|
14
|
+
#
|
15
|
+
# The important thing to notice is that the params passed to the `thumbnail` method
|
16
|
+
# are not passed into the final render and so will not result in the image being inserted
|
17
|
+
# with the custom size params.
|
18
|
+
#
|
19
|
+
# Thie Renderable class solves this by intercepting all calls that return an object
|
20
|
+
# that responds to #render and wrapping them in a Renderable instance that maintains
|
21
|
+
# any params passed to them. So in the above method chain results gives:
|
22
|
+
#
|
23
|
+
# context.image #=> Renderable.new(page.image, {})
|
24
|
+
# context.image.thumbnail #=> Renderable.new(page.image.thumbnail, {width: 50, height: 50})
|
25
|
+
#
|
26
|
+
# and so the final render call will be made to the final Renderable instance which will
|
27
|
+
# invoke page.image.thumbnail#render with the :width & :height locals it's saved from the
|
28
|
+
# original template call, equivalent to:
|
29
|
+
#
|
30
|
+
# page.image.thumbnail.render(__format, {width: 50, height: 50}, ...)
|
31
|
+
|
32
|
+
module Spontaneous::Output
|
33
|
+
# A convenience method that tests to see if an object is 'renderable'
|
34
|
+
# that is it responds to #render
|
35
|
+
# and if so wraps it in a Renderable object with the given locals
|
36
|
+
def self.Renderable(obj, locals)
|
37
|
+
if obj.respond_to?(:render)
|
38
|
+
Renderable.new(obj, locals)
|
39
|
+
else
|
40
|
+
obj
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Renderable < BasicObject
|
45
|
+
|
46
|
+
def initialize(target, template_params = {})
|
47
|
+
@target, @template_params = target, (template_params || {})
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing(name, *args)
|
51
|
+
result = nil
|
52
|
+
locals = __extract_locals__(args)
|
53
|
+
|
54
|
+
if ::Kernel.block_given?
|
55
|
+
# If we are passed a block then we want to make sure that any values
|
56
|
+
# yielded from that block are wrapped in Renderable instances
|
57
|
+
# if appropriate
|
58
|
+
block = ::Proc.new
|
59
|
+
result = @target.send(name, *args) do |*block_args|
|
60
|
+
renderable_args = block_args.map { |arg| ::Spontaneous::Output::Renderable(arg, locals) }
|
61
|
+
block.call(*renderable_args)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
result = ::Spontaneous::Output::Renderable(@target.send(name, *args), locals)
|
65
|
+
end
|
66
|
+
result
|
67
|
+
end
|
68
|
+
|
69
|
+
def respond_to?(method_name, include_private = false)
|
70
|
+
@target.respond_to?(method_name, include_private) || super
|
71
|
+
end
|
72
|
+
|
73
|
+
alias_method :respond_to_missing?, :respond_to?
|
74
|
+
|
75
|
+
def render(format = :html, locals = {}, parent_context = nil)
|
76
|
+
@target.render(format, __render_locals__(locals), parent_context)
|
77
|
+
end
|
78
|
+
|
79
|
+
def render_using(renderer, format = :html, locals = {}, parent_context = nil)
|
80
|
+
@target.render_using(renderer, format, __render_locals__(locals), parent_context)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def __render_locals__(locals)
|
86
|
+
return @template_params if locals.nil?
|
87
|
+
locals.merge(@template_params || {})
|
88
|
+
end
|
89
|
+
|
90
|
+
# This is a non-destructive version of activesupport's Array#extract_options!
|
91
|
+
def __extract_locals__(args)
|
92
|
+
if args.last.is_a?(::Hash)
|
93
|
+
args.last
|
94
|
+
else
|
95
|
+
{}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Spontaneous::Output
|
2
|
+
module Store
|
3
|
+
autoload :Backend, 'spontaneous/output/store/backend'
|
4
|
+
autoload :File, 'spontaneous/output/store/file'
|
5
|
+
autoload :Moneta, 'spontaneous/output/store/moneta'
|
6
|
+
autoload :Revision, 'spontaneous/output/store/revision'
|
7
|
+
autoload :Store, 'spontaneous/output/store/store'
|
8
|
+
autoload :Transaction, 'spontaneous/output/store/transaction'
|
9
|
+
|
10
|
+
# Initializes a new store with the provided options
|
11
|
+
# Uses moneta as a unified interface to except in the special case of the :File
|
12
|
+
# store which needs to work in a way compatible with the use of
|
13
|
+
# an frontend HTTP proxy, e.g. Nginx
|
14
|
+
def self.new(backend_class, options = {})
|
15
|
+
backend = case backend_class
|
16
|
+
when :File
|
17
|
+
File.new(options[:root])
|
18
|
+
else
|
19
|
+
Moneta.new(backend_class, options)
|
20
|
+
end
|
21
|
+
Store.new(backend)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Spontaneous::Output::Store
|
2
|
+
class Backend
|
3
|
+
unless defined?(STATIC_PATH)
|
4
|
+
STATIC_PATH, PROTECTED_PATH, DYNAMIC_PATH = %w(static protected dynamic).map(&:freeze)
|
5
|
+
end
|
6
|
+
|
7
|
+
def store_static(revision, key, template, transaction = nil)
|
8
|
+
store(revision, STATIC_PATH, key, template, transaction)
|
9
|
+
end
|
10
|
+
|
11
|
+
def store_protected(revision, key, template, transaction = nil)
|
12
|
+
store(revision, PROTECTED_PATH, key, template, transaction)
|
13
|
+
end
|
14
|
+
|
15
|
+
def store_dynamic(revision, key, template, transaction = nil)
|
16
|
+
store(revision, DYNAMIC_PATH, key, template, transaction)
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_static(revision, key)
|
20
|
+
load(revision, STATIC_PATH, key)
|
21
|
+
end
|
22
|
+
|
23
|
+
def load_protected(revision, key)
|
24
|
+
load(revision, PROTECTED_PATH, key)
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_dynamic(revision, key)
|
28
|
+
load(revision, DYNAMIC_PATH, key)
|
29
|
+
end
|
30
|
+
|
31
|
+
def output_key(output, dynamic = false)
|
32
|
+
path = output.page.path
|
33
|
+
ext = output.extension(dynamic)
|
34
|
+
case path
|
35
|
+
when Spontaneous::SLASH
|
36
|
+
"/index#{ext}"
|
37
|
+
else
|
38
|
+
"#{path}#{ext}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def store(revision, partition, path, template, transaction)
|
45
|
+
raise NotImplementedError
|
46
|
+
end
|
47
|
+
|
48
|
+
def load(revision, partition, path)
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Spontaneous::Output::Store
|
4
|
+
# The basic template storage backend.
|
5
|
+
#
|
6
|
+
# It stores static, protected & dynamic templates in different
|
7
|
+
# directories so that a reverse proxy conf can be pointed directly
|
8
|
+
# at the `static` area and (in theory) protected templates could
|
9
|
+
# be served using a "sendfile" header.
|
10
|
+
class File < Backend
|
11
|
+
|
12
|
+
def initialize(root)
|
13
|
+
@root = root
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_revision(revision, keys)
|
17
|
+
ensure_dir revision_path(revision)
|
18
|
+
end
|
19
|
+
|
20
|
+
def revisions
|
21
|
+
dirs = ::Dir.entries(@root).select { |dir| /^[0-9]+$/ === dir }
|
22
|
+
dirs.map { |dir| dir.to_i(10) }.sort
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete_revision(revision, keys = nil)
|
26
|
+
if (dir = revision_path(revision)) && ::File.exist?(dir)
|
27
|
+
::FileUtils.rm_r(dir)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def store(revision, partition, key, template, transaction)
|
34
|
+
::File.open(path!(revision, partition, key, transaction), 'wb') { |f| f.write(template) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def load(revision, partition, key)
|
38
|
+
read(path(revision, partition, key))
|
39
|
+
end
|
40
|
+
|
41
|
+
def read(path)
|
42
|
+
return nil unless ::File.exist?(path)
|
43
|
+
::File.open(path, 'r:UTF-8')
|
44
|
+
end
|
45
|
+
|
46
|
+
def pad_revision(revision)
|
47
|
+
revision.to_s.rjust(5, "0")
|
48
|
+
end
|
49
|
+
|
50
|
+
def path!(revision, partition, key, transaction)
|
51
|
+
ensure_path path(revision, partition, key, transaction)
|
52
|
+
end
|
53
|
+
|
54
|
+
def path(revision, partition, path, transaction = nil)
|
55
|
+
transaction.push(key(revision, partition, path)) if transaction
|
56
|
+
::File.join(revision_path(revision), partition, path)
|
57
|
+
end
|
58
|
+
|
59
|
+
def revision_path(revision)
|
60
|
+
::File.join(@root, pad_revision(revision))
|
61
|
+
end
|
62
|
+
|
63
|
+
def ensure_path(path)
|
64
|
+
ensure_dir ::File.dirname(path)
|
65
|
+
path
|
66
|
+
end
|
67
|
+
|
68
|
+
def ensure_dir(dir)
|
69
|
+
FileUtils.mkdir_p(dir) unless ::File.exist?(dir)
|
70
|
+
dir
|
71
|
+
end
|
72
|
+
|
73
|
+
def key(revision, partition, path)
|
74
|
+
::File.join(pad_revision(revision), partition, path)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|